违反里氏替换原则

Liskov substitution principle violation

来自 Wikipedia

Liskov's notion of a behavioral subtype defines a notion of substitutability for objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g. correctness).

假设以下 class 层次结构:

  1. 基础摘要 class - AnimalWithFur。它有一个只读的 属性 furColor,在后继者中被覆盖。
  2. Base class 的继任者 - Cat,它覆盖 furColor 和 returns gray.
  3. Cat 的继任者 - Tiger,它覆盖 furColor 和 returns striped.

然后我们用Cat类型的参数声明一个方法(不是AnimalWithFur)。
向该方法发送 Tiger 实例是否违反了 SOLID 中的 L

简答:不一定。根据您提供的信息,我不会说。对我来说,关键是你没有说出想象中的新方法应该做什么。

您可能认为新方法中要求的行为比 class 层次结构的关注点更重要。

实现此目的的一种方法是从传入的实例/参数中为新方法所需的行为定义一个接口。

然后 class 您可能想要传递给该方法的任何一个都可以实现该接口,并且您打破了继承层次结构的关注点,转而关注行为的一致性。

严格来说,是的。 Liskov 的 wiki 文章总结说:

"...in a program...without altering any of the desirable properties of that program"

如果您回到 Barbara Liskov 的 original paper,它的措辞 字面上更严格3.3。 类型层次结构:

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2

(强调我的)

因此,如果您将 Cat 的一个实例替换为另一个执行不同操作的实例,即返回 stripped 而不是灰色,那么这就是原始意义上的 Liskov 违规,因为可以很容易地定义一个程序依赖于颜色为灰色,这里:

program(Cat c){
   println(c.furColor);
}

如果您传递一个 Tiger 代替 Cat,该程序的行为 将会改变

但是,在正常的LSP应用方式中,如果不添加额外的前置条件或后置条件,则不属于违规。这是一个更实际、更不学术的定义,因为人们认为当用另一种具体类型替换一个具体类型的实例时,您 do 打算改变程序的行为,同时保持该程序的理想属性.因此,假设客户端代码可以像任何其他颜色一样处理剥离,并且程序的 "desirable" 属性 不需要灰色,那么它就不会违反。

您的问题很好地描述了为什么要使用 class 组合而不是 class 继承。首先,您的代码不合逻辑 - Tiger 在您的意义上不是 CatTigerCats family. 之一,从代码的角度来看,重写和完全替换父 class 的行为,这实际上是 liskov 替换违规 - 你的 Cat class 意味着用一些具体的颜色定义的猫,应用程序希望分别使用它,但你是用不一致的类型覆盖它并改变行为。 如果你能正确地描述类型层次结构,你会得到没有实现 furColor 的抽象类型 Cat,类型 TigerHomeCat,但是 HomeCat 可以有不同的颜色,不是吗?

如果你想要一个 LS 违规的简单例子,例如: 您正在使用自定义实现扩展 List 接口,返回大小始终为 10,但内部对象的数量不同。每个普通应用程序都希望使用 for 语句处理列表,但会出现不可预测的行为,因为您违反了 LS 原则,并且 List 对象的行为与预期不符。