`

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

 
阅读更多

 

        本文转自:http://blog.csdn.net/brookes/article/details/1896758

 

        里氏替换原则(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()<r.getWidth()))
    
{
        r.setHeight(r.getHeight()
++)
    }

    
    
return r;
}

        如果传递给IncreaseHeight的是一个Rectangle(长宽不同)的对象的话,没问题;如果传递一个Square对象。。。谁都知道会是个什么结果!出现这个问题的原因就是这个继承结构的设计违反了LSP原则:Square类对Height和Weight的处理和Rectangle逻辑不同不同,Rectangle单独改变Widtht和Height,而Square必须同时改变Width和Height。所以,Square和Rectangle之间的继承关系是不能成立的。可以增加一个抽象类Quadrangle,定义四边形的公共方法,Square和Rectangle都从Quadrangle继承这些方法,同时可以添加自己特有的方法:

 

        其实,不只是这个“经典”的问题反映了现实世界中概念和OO概念的区别,很多情况都需要在做OOD的时候仔细考虑。只有符合了LSP的规定,才可能实现OCP。以下几条可作为实践经验:

        1.从抽象类继承,不要从实体类继承。因为实体类会具有和特定实体相关的方法,这些方法在子类中可能不会有用。

        2.使用契约式编程方法。DBC(Design by Contract)把类和其客户之间的关系看作是一个正式的协议,明确各方的权利和义务。在父类里定义好子类需要实现的功能,而子类只要实现这些功能即可。

        3.所有派生类的行为功能必须和客户程序对其基类所期望的保持一致。上面的例子就是违背了这一条。其实,这是OO在继承和重写时的基本要求。

分享到:
评论

相关推荐

    OOD设计基本原则 面向对象设计必备

    里氏替换原则(Liskov Substitution Principle)是 OOD 设计基本原则之一,它规定一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它察觉不出基类对象和子类对象的区别。LSP 是继承复用的基石,...

    软件设计的七大原则(OOD)

    其中包括SRP(单一职责原则)、OCP(开闭原则)、LSP(里氏替换原则)、ISP(接口分离原则)、DIP(依赖倒置原则)、CRP(组合/聚合复用原则)和PLK(最小知识原则)。 一、SRP(Single Responsibility Principle)...

    swift-面向对象设计的设计原则和设计模式

    面向对象设计的原则主要有四个:单一职责原则(Single Responsibility Principle, SRP)、开闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)和依赖倒置原则(Dependency...

    OOD原则 GRASP GOF

    3. **里氏替换原则(Liskov Substitution Principle, LSP)**: 子类对象应当可以替换掉其基类对象,并在任何基类能够正确工作的地方,子类也能够正确工作。这保证了程序的稳定性和兼容性。 4. **接口隔离原则...

    面向对象的设计原则详解

    3. 里氏替换原则(Liskov Substitution Principle, LSP): 根据里氏替换原则,任何基类可以出现的地方,其子类也应该能够被无缝替换,而不会影响程序的正确性。这保证了代码的可扩展性和一致性。 4. 依赖倒置原则...

    OOD.rar_ood

    7. **原则与指导方针**:OOD遵循一些基本原则,如 SOLID 原则,包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。这些原则有助于创建易于理解和维护的...

    OOD启思录(高清)

    8. ** SOLID原则**:SOLID是面向对象设计的五个基本原则,包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。这些原则指导我们编写易于维护和扩展的代码...

    面向对象设计原则:突破软件工程学习的质的飞跃

    2. **里氏替换原则(Liskov Substitution Principle, LSP)**:该原则指出,子类型必须能够替换其基类型。这意味着如果一个程序使用的是父类的一个实例,那么用子类的实例代替它时,程序的行为不应该改变。这一原则...

    面向对象七大基本设计原则.pdf

    3. 里氏替换原则(Liskov Substitution Principle, LSP):子类应该能够替换它们的基类。即派生类应该能够完全替代基类出现在程序中,同时保持程序的正确性。 4. 依赖倒置原则(Dependency Inversion Principle, ...

    面向对象设计的基本原则

    3. **里氏替换原则 (Liskov Substitution Principle, LSP)** - 子类型必须能够替换掉它们的基类型。这意味着子类可以完全替代父类在程序中的角色,而不会导致任何错误或需求的改变。这有助于保持代码的稳定性和可...

    99丨总结回顾:在实际软件开发中常用的设计思想、原则和模式1

    SOLID原则包括单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。其中,SRP确保每个类只有一个明确的责任,OCP让代码对扩展开放、对修改关闭,LSP保证子类...

    讲述了面向对象设计的原则

    里氏替换原则(Liskov Substitution Principle, LSP) 里氏替换原则强调子类必须能够替换它们的基类。具体来说,在程序中凡是使用基类的地方,都可以用子类的对象替代,而不会影响程序的正确性和稳定性。这一原则...

    Uncle Bob OOD Principles

    3. **里氏替换原则(Liskov Substitution Principle, LSP)**:子类必须能够替换其基类。这意味着子类型应当可以无差别地使用在父类型出现的地方,而不会导致程序行为的改变。遵循LSP有助于保持代码的稳定性和一致性...

    面向对象设计的基本原则和实践建议

    3. **里氏替换原则(Liskov Substitution Principle, LSP)**:子类型必须能够替换掉它们的基类型。这意味着在所有使用基类型的地方,都可以安全地使用子类型,而不会影响程序的正确性。遵守此原则能确保程序的稳定...

    61条面向对象设计的经验原则

    3. **里氏替换原则(Liskov Substitution Principle, LSP)**:子类型必须能够替换它们的基类型,而不影响程序的正确性。这确保了继承的使用不会破坏代码的稳定性。 4. **接口隔离原则(Interface Segregation ...

    ood-solid:面向对象设计的SOLID原理

    这五条原则分别是单一职责原则(Single Responsibility Principle, SRP)、开闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)、接口隔离原则(Interface Segregation ...

    61条面向对象设计的经验面试题

    3. **里氏替换原则(Liskov Substitution Principle, LSP)**:子类型必须能够替换它们的基类型,而不影响程序的正确性。这保证了代码的可替换性和兼容性。 4. **接口隔离原则(Interface Segregation Principle, ...

    关于软件设计的问题和方法

    软件设计过程中,设计师通常遵循一些基本原则,例如单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。这些原则有助于保持代码的可读性、可扩展性和可维护性...

    实用面向对象软件工程教程

    设计时需遵循一些基本原则,如单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP),它们有助于提高代码的可读性、可扩展性和可维护性。 “实用面向对象软件...

    UML和模式应用面向对象分析与设计导论

    此外,书中可能还会讨论到面向对象的原则和最佳实践,比如SOLID原则,这是由五个基本的设计原则组成,分别是单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)...

Global site tag (gtag.js) - Google Analytics