OOD设计原则之里氏替换原则(LSP)

转自:http://blog.csdn.net/Brookes/archive/2007/11/21/1896758.aspx 里氏替换原则(Liskov Substitutiion Principle,LSP)被称作继承复用的基石,它的提出甚至要早于OCP。不过遗憾的是,由于对这一原则的理解各不相同,经过多次的翻译、转述,LSP成了OOD设计原则中争议最多的话题之一。 其实早在1987年的OOPSLA大会上,麻省理工学院(MIT)计算机科学实验室的Liskov女士就发表了经典文章Data Abstraction and Hierarchy,其中提出了以她名字命名的Liskov替换原则(The Liskov Substitution Principle),简称LSP。该原则说明了什么时候该使用继承,什么时候不该使用以及为什么。一年后此文发表在ACM的SIGPLAN Notices杂志上,更是影响深远。Liskov在这篇文章中写到: A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.What is wanted here is something like the following substitution property: 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 then S is a subtype of T. 意思是:“类型层次由子类型和超类型(也就是父类)组成,直觉告诉我们,子类型的含义就是该类型的对象提供了另外一个类型(超类型)的对象的所有行为功能,并有所扩充。这里需要如下的替换性质:若对于每一个类型S的对象o1,都存在一个类型T的对象o2,使得在所有针对T编写的程序P中,用o1替换 o2后,程序P的行为功能不变,则S是T的子类型。”这就是LSP的最初含义。 而著名技术作家Robert Martin在1996年为《C++ Reporter》写了一篇题为《The The Liskov Substitution Principle》的文章,专门介绍LSP。在Martin的文章中,他给了LSP一个解释: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. 意思是:“使用指向基类的指针或引用的函数,必须能够在不知道具体派生类对象类型的情况下使用它们。”在2002年,Martin在他出版的《Agile Software Development Principles Patterns and Practices》一书中,又进一步简化为: Subtypes must be substitutable for their base types。子类必须能替换成它们的父类。 也是正是由于这许多版本的存在在加上翻译,转述等等问题,最终导致了LSP的多种理解(不深究了)。按照我的理解,上面Liskov的表述虽然拗口,但是也不难明白。再加上Martin的解释,LSP原则就很清楚了。 LSP说的其实是在构建类的继承结构的过程中需要遵循的基本原则,什么时候改用,什么时候不能用,避免继承的滥用。LSP和OCP是相关联的,也可以说是OCP的基本保证。试想,如果某个函数使用了指向基类的指针或引用,但是类的设计却违背了LSP原则,那么这个函数就必须了解该基类的所有派生类。这个函数显然违背开放-封闭原则OCP,因为一旦新构建该基类的子类,此函数就需要修改。 我也来说那个经典的”正方形不是矩形“的问题。在数学的世界里,正方形当然是矩形。用OO的数据,正方形和矩形之间是IS-A的关系——这个关系正好是OO初级教程里说的确定继承关系的依据。因此,理所当然的,正方形类然Square应该继承长方形类Rectangle: public class Rectangle ...{ private long width; private long height; public void setWidth(long width)...{ this.width=width; } public void setHeight(long height)...{ this.height=height; } public long getWidth()...{ return width; } public long getHeight()...{ return height; } }; public class Square : Rectangle ...{ public void setWidth(long width)...{ this.width=width; this.height=width; } public void setHeight(long height)...{ this.width=width; this.height=width; } }; 假设有这么一个函数: public Rectangle IncreaseHeight(Rectangle r) ...{ while(r.getHeight()

没有更多推荐了,返回首页