这是在一次采访中问我的。
我回答他说,对于相同的输入集,父母和孩子都应该产生相同的输出集。如果子节点想要扩展父节点的功能,它应该只在父节点支持范围之外的新输入上执行。这样,孩子将维持其父母签订的合同。
我给他举了一个例子,一个api可能正在使用这样的父级
if(parent.getOutput(10) == 5){/*do something */}
如果这个孩子在这里产生了不同的输出,那么这个孩子就违反了它的父母签订的合同。
他对我的回答不满意,并告诉我这是简单的压倒一切,不违反LSP。所以,我只是想确认一下,我的理解是否正确。
不,这是不正确的。
Liskov替换原则的要点是,根据相关合同,子类应该能够以与父类相同的方式使用。它不需要为相同的输入生成相同的输出。
在不可避免的动物主题中,下面是一个示例,其中方法为相同的输入返回不同的输出:
class Dog {
String getNoise() {
return "WOOF WOOF!!!"
}
}
class FrenchBulldog extends Dog {
String getNoise() {
return "mrfff!"
}
}
法国斗牛犬
可以在任何上下文中代表任何狗
,行为方式相同,因此不违反LSP。
如果您改为创建如下内容:
class Hotdog extends Dog implements Edible {
String getNoise() {
throw new NotImplementedException("I am actually a sausage");
}
String eat() {
return "Delicious";
}
}
然后它就不能再代替狗了。与Dog
s和FrenchBulldog
s完美配合的代码不再以应有的方式工作。无论狗狗发出什么样的噪音,任何一个狗美容师或犬舍都知道该做什么,但是如果你带了热狗,他们会非常困惑。这违反了LSP。
我认为是这样的,因为子类在被替换时的行为不像基类,而且它抛出了一个RuntimeException? 我不完全确定,我想知道我的假设是否正确
以下代码是否直接违反了Liskov替换原则: 子类不应破坏父类的类型定义。 结果如下: 致命错误:b::baz(Foo $foo)的声明必须与a::baz(Baz $baz)兼容
有一个很好的例子,在圆-椭圆问题中违反了Liskov替换原理。 下面是一个流行的SO答案的措辞(尽管是矩形和正方形): 在数学中,是一个。实际上,它是矩形的特化。“is a”使您希望使用继承来对此进行建模。但是,如果在代码中使派生自,那么应该可以在任何需要的地方使用。这导致了一些奇怪的行为。 想象一下,您的矩形基类上有和方法;这似乎完全符合逻辑。但是,如果矩形引用指向,则和没有意义,因为设置一个将
我试图通过反复阅读维基百科条目来确定我对上述原则的理解。 撇开仍然让我悲伤的协变和逆变的概念不谈,wikipedia还提到超类型的不变量必须保留在子类型和历史约束或历史规则中。基于最后两个概念,我提出了一个小例子: 所以我的问题是:基于上述两个概念,我用这个例子是否违反了原则?若否,原因为何? 事先非常感谢。
现在,让我们来看看“燃料”类: 以上是完成的所有抽象类,现在让我们看看具体的实现。首先,fuel的两个具体实现,包括一些贫血接口,以便我们可以正确地键入-提示/嗅探它们: 最后,我们有了车辆的具体实现,它确保使用正确的燃料类型(接口)为特定的车辆类别加油,如果不兼容则抛出异常: null
我正在详细学习LSP,我确实理解为什么强化先决条件违反了这一原则(使用来自http://www.ckode.dk/programming/solid-principles-part-3-liskovs-substitution-principle/#contravariance): 在这里,我清楚地看到,对于基类有效的东西对于它的派生类将失败。换句话说,在不改变行为的情况下,我无法用基类的导数替换