liskov替换原则和接口隔离原则的区别

The difference between liskov substitution principle and interface segregation principle

里氏代换原则(LSP)和接口隔离原则(ISP)有核心区别吗?最终,两者都保证设计具有通用功能的界面,并在您有特殊用途的功能时引入新界面。

LSP:子类型必须遵守它承诺的契约。

ISP:调用者不应该依赖比它需要更多的基类型接口。

适合的地方:如果您应用 ISP,则您只使用接收器完整接口的一部分。但根据 LSP,接收方仍必须遵守该切片。

如果申请ISP失败,就有可能违反LSP。因为“这个方法无关紧要,实际上不会被调用。”

它们都是 SOLID 原则

  • LSP(Liskov 替换):此原则要求您确保所有子 class 具有与父 class 相同的行为。 例如:如果你有一个 Device class 并且它有函数 callBaba() 可以得到你父亲的 phone 号码然后给他打电话,所以你必须确保 callBaba() 方法在 Device 的所有子 class 中做同样的工作。如果 Device 的任何子 classes 在 callBaba() 中有其他行为,这意味着你破坏了 LSP

Example of code that breaks Liskov Principle.

class Device {
    func callBaba() {
        print("I will find your father and I will call him")
    }
}

class Calculator: Device {
    override func callBaba() {
      print("Sorry, I don't have this functionality ")
    }
}

The Solution

interface CanCall {
            func callBaba()
        }
        class Device {
            // all basic shared functions here.
        }

        class Calculator: Device {
            // all functions that calculator can do + Device
        }
        class SmartPhone: Device implements CanCall {
            // all smartphone stuff
            func callBaba() {
                print("I will find your father and I will call him")
            }
        }
  • ISP(Interface Segregation):要求你为不同的职责创建不同的接口,换句话说,不要将不相关的行为分组在一个接口中, 如果你破坏了 ISP 你已经有了一个有很多职责的接口,实现者不需要所有这些东西

this breaks ISP principle because it has two different responsibilities

  protocol Animal {
        func fly()
        func eat()
    }

The Solution

protocol Flyable {
    func fly()
}
protocol Feedable {
    func eat()
}

LSP 管理 parent 和 child classes 之间的关系(即层次关系)。它告诉您 如何 实施 API。

ISP 管理 parent 和客户端 class 之间的关系(即 producer/consumer 关系)。它告诉您何时 实施API。

考虑一个有一百个方法的接口。一个 child class 可以执行所有 hundred 而不会违反其中任何一个定义的契约,从而满足 Liskov 替换;但很难想象每个客户端都需要所有这些方法,因此几乎肯定会违反接口隔离。

反之,只有一个方法的接口肯定满足接口隔离;但是如果一个实现不遵守那个方法契约,那么 Liskov 替换就被违反了。

另请参阅:LSP vs DIP