面向对象的设计原则四 - 里氏代换原则

动机

当我们设计程序模块时,我们会创建一些类层次结构,然后我们通过扩展一些类来创建它们的子类。

我们必须确保子类只是扩展而没有替换父类的功能,否则当我们在已有程序模块中使用它们时将会产生不可预料的结果。

里氏代换原则表明当一个程序模块使用基类时,基类的引用可以被子类替换而不影响模块的功能。

里氏代换原则

基类完全能够被子类替代而不影响模块的功能。

实例

对于多态来说里氏代换原则好像是很显然的事情,例如:

Java代码 复制代码 收藏代码
  1. publicvoiddrawShape(Shapes){
  2. //Codehere.
  3. }
public void drawShape(Shape s) {
// Code here.
}

对于Shape的任何子类来说,drawShape方法都应该能很好的工作。我们必须小心的实现子类以免无意中违反了里氏代换原则,如果一个函数不满足里氏代换原则,那么它可能必须显式地引用子类对象,这样的函数同样违反了开闭原则,因为当添加新的子类时,必须修改它。

考虑下面的矩形类:

现在,如果有个正方形呢?显然正方形是一个矩形,所以我们应该让正方形继承矩形类,是这样吗?我们看一下!

注意:

  • 正方形不需要同时具有宽和高属性,但是它还是从矩形继承了这些属性。所以,每个正方形都浪费了一点空间,但这不是我们关注的主要问题。
  • 继承而来的setWidth()和setHeight()方法实际上对于正方形是不合适的,因为正方形的宽和高是相等的。所以我们需要重写setWidth()和setHeight()方法,这可能暗示着在这儿并不适合使用继承。

下面是Square类:

一切看上去都很好,但是注意下面的代码:

测试程序输出:

看起来我们违反了里氏代换原则,问题在哪儿?testLSP()方法合理的假设当一个矩形的宽改变时,它的高度不变。当传递一个正方形对象时,该方法却违反了里氏代换原则。从数学上看,正方形是一个矩形,但是一个正方形对象却不是矩形对象,因为一个正方形对象的行为和一个矩形对象的行为不一致。从行为上来说,正方形不是矩形!里氏代换原则清晰的说明,IS-A关系是对于所有的行为来说的,为了遵循里氏代换原则,子类的行为必须和客户端使用的基类的行为一致。

子类不能比基类具有更多的约束,因为必须在任何可以使用基类的地方使用子类,如果子类比基类有更多的约束,那么就会出现基类可用,但却违反了子类约束的情况。

总结

里氏代换原则是对开闭原则的扩展,它表明我们在创建基类的新的子类时,不应该改变基类的行为。

你可能感兴趣的:(面向对象)