2009年3月17日星期二

解耦合手段之七:Liskov代换原则

周二,美国计算机协会宣布,来自麻省理工学院的女教授芭芭拉·利斯科夫(Barbara Liskov)获得本年度图灵奖以及25万美元的奖金,她的贡献是让计算机程序更加可靠、安全和易于使用。(新闻
Barbara Liskov 是美国第一位获得计算机博士学位的女性。她的研究为模块化编程和面向对向编程的产生奠定了基础。除此之外,Barbara的另一个为人们所熟知的供献是她定义了Liskov substitution principle ,我们且称之为“Liskov代换原则”。它为子类型化或者说面向对向中的“继承”定义了重要的原则。
Liskov代换原则的原文是挺难看懂的:
Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

幸好有Bob大叔做好事,早就写了一篇相对容易理解一点的文章来解释Liskov Substitution Principle:
使用指向基类(超类)的引用的函数,必须能够在不知道具体派生类(子类)对象类型的情况下使用它们。

举一个例子:
有一个基类Rectangle(矩形)。按常理来说,正方形(Square)也是一个矩形,那么Square类可以从Rectangle类继承而来。而事实是这样的么?假设有一个函数:
void g(Rectangle& r)
{
   r.SetWidth(5);
   r.SetHeight(4);
   assert(r.GetWidth() * r.GetHeight()) == 20);
}
很显然,这个函数对于传入的基类Rectangle变量来讲是正确的。然而如果传如入的变量是Square类型就不正确了。因此,Square与Rectangle之间的继承关系违反了Liskov代换原则。
那么为什么会这样呢?从常识(或几何定义)的角度来看,正方形是一个矩形;然而从行为的角度看,正方形不是一个矩形!因为正方形的行为与矩形有所不同。如果一定要让正方形继承于矩形,那么正方形的行为必须修改矩形的行为。可以看出,这同时也违反了“开闭原则 ”。

Liskov代换原则与“契约式设计(Design by Contract)”也有很紧密的联系。在Design by Contract的模式下对于一个类的方法有precondition(先决条件)和postcondition(后置条件)两个概念。Precondition是为了使用这个方法而必须事先满足的条件;Postcondition是这个方法必须保证它被使用以后成立的条件。由Liskov代换原则我们可以得到:
.子类不应要求比基类更高的precondition。
.子类不应弱化基类的postcondition。
例如上面的Rectangle类里SetWidth方法的一个postcondition是矩形的Height不应发生变化。这个postcondition在Square中就不能满足。

发现中文维基中没有Liskov代换原则的条目,把它补上:


没有评论:

发表评论