违反里氏替换原则
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 层次结构:
- 基础摘要 class -
AnimalWithFur
。它有一个只读的 属性 furColor
,在后继者中被覆盖。
- Base class 的继任者 -
Cat
,它覆盖 furColor
和 returns gray.
- 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
在您的意义上不是 Cat
,Tiger
是 Cats family.
之一,从代码的角度来看,重写和完全替换父 class 的行为,这实际上是 liskov 替换违规 - 你的 Cat
class 意味着用一些具体的颜色定义的猫,应用程序希望分别使用它,但你是用不一致的类型覆盖它并改变行为。
如果你能正确地描述类型层次结构,你会得到没有实现 furColor 的抽象类型 Cat
,类型 Tiger
和 HomeCat
,但是 HomeCat
可以有不同的颜色,不是吗?
如果你想要一个 LS 违规的简单例子,例如:
您正在使用自定义实现扩展 List
接口,返回大小始终为 10,但内部对象的数量不同。每个普通应用程序都希望使用 for 语句处理列表,但会出现不可预测的行为,因为您违反了 LS 原则,并且 List 对象的行为与预期不符。
来自 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 层次结构:
- 基础摘要 class -
AnimalWithFur
。它有一个只读的 属性furColor
,在后继者中被覆盖。 - Base class 的继任者 -
Cat
,它覆盖furColor
和 returns gray. - 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
在您的意义上不是 Cat
,Tiger
是 Cats family.
之一,从代码的角度来看,重写和完全替换父 class 的行为,这实际上是 liskov 替换违规 - 你的 Cat
class 意味着用一些具体的颜色定义的猫,应用程序希望分别使用它,但你是用不一致的类型覆盖它并改变行为。
如果你能正确地描述类型层次结构,你会得到没有实现 furColor 的抽象类型 Cat
,类型 Tiger
和 HomeCat
,但是 HomeCat
可以有不同的颜色,不是吗?
如果你想要一个 LS 违规的简单例子,例如:
您正在使用自定义实现扩展 List
接口,返回大小始终为 10,但内部对象的数量不同。每个普通应用程序都希望使用 for 语句处理列表,但会出现不可预测的行为,因为您违反了 LS 原则,并且 List 对象的行为与预期不符。