我听说利斯科夫替换原则(LSP)是面向对象设计的基本原则。它是什么?它的一些使用例子是什么?
当前回答
一些补充:我想知道为什么没有人写基类的不变量、前提条件和后置条件,这些派生类必须遵守。 对于派生类D来说,基类B完全可转换,类D必须服从某些条件:
基类的内变体必须由派生类保留 派生类不能加强基类的先决条件 派生类不能削弱基类的后置条件。
因此派生类必须知道基类施加的上述三个条件。因此,子类型的规则是预先确定的。这意味着,只有当子类型遵守某些规则时,才应该遵守'IS A'关系。这些规则,以不变量、前置条件和后置条件的形式,应该由正式的“设计契约”来决定。
关于这个问题的进一步讨论可以在我的博客:利斯科夫替换原理
其他回答
利斯科夫替换原理
被重写的方法不应该保持为空 被重写的方法不应该抛出错误 基类或接口行为不应该因为派生类行为而进行修改(重做)。
以Board数组的形式实现ThreeDBoard会有用吗?
也许你想把不同平面上的ThreeDBoard切片作为一个板。在这种情况下,您可能希望为Board抽象出一个接口(或抽象类),以允许多种实现。
就外部接口而言,您可能希望为TwoDBoard和ThreeDBoard提取一个Board接口(尽管上述方法都不适合)。
它指出,如果C是E的子类型,则E可以替换为C类型的对象,而不会改变或破坏程序的行为。简单地说,派生类应该可以替代它们的父类。例如,如果一个农民的儿子是农民,那么他可以代替他的父亲工作,但如果一个农民的儿子是板球运动员,那么他就不能代替他的父亲工作。
违反的例子:
public class Plane{
public void startEngine(){}
}
public class FighterJet extends Plane{}
public class PaperPlane extends Plane{}
在给定的例子中,fighter和PaperPlane类都扩展了包含startEngine()方法的Plane类。所以很明显,战斗机可以启动引擎,但纸飞机不能,所以它破坏LSP。
PaperPlane类虽然扩展了Plane类,但应该可以替代Plane类,但它不是Plane实例可以被替换的合格实体,因为纸飞机不能启动引擎,因为它没有引擎。好的例子是,
受人尊敬的例子:
public class Plane{
}
public class RealPlane{
public void startEngine(){}
}
public class FighterJet extends RealPlane{}
public class PaperPlane extends Plane{}
关于LSP的一个很好的例子(在我最近听到的播客中,Bob叔叔给出了一个例子)是,有时候在自然语言中听起来正确的东西在代码中却不太适用。
在数学中,正方形是长方形。实际上,它是矩形的专门化。“is a”使您想用继承来建模。然而,如果在代码中你从Rectangle派生出Square,那么Square应该可以在任何你想要Rectangle的地方使用。这就导致了一些奇怪的行为。
假设你在你的Rectangle基类上有SetWidth和SetHeight方法;这似乎完全合乎逻辑。然而,如果你的矩形引用指向一个正方形,那么SetWidth和SetHeight没有意义,因为设置一个会改变另一个来匹配它。在这种情况下,Square未能通过矩形的利斯科夫替换测试,并且让Square继承Rectangle的抽象是一个糟糕的抽象。
你们都应该看看其他用励志海报解释的无价的坚实原则。
LSP是关于类的契约的规则:如果基类满足契约,则LSP派生的类也必须满足该契约。
在Pseudo-python
class Base:
def Foo(self, arg):
# *... do stuff*
class Derived(Base):
def Foo(self, arg):
# *... do stuff*
如果每次在派生对象上调用Foo,它给出的结果与在Base对象上调用Foo完全相同,只要arg是相同的。