`

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设计基本原则整理.doc

    OOD设计基本原则 一. 开-闭原则 :我们改变不了历史,但我们可以改变未来。

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

    OCP原则 里氏替换原则 依赖倒置原则 接口隔离原则 聚合与继承原则 单一职责原则 Separation of concerns Principle Pareto Principle (帕雷多原则 80/20原则)

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

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

    OOD面试应对方法 SOLID原则.mp4

    应届生及亚马逊面试必考,IT求职必备基础。...L - Liskov substitution principle 里氏替换原则 I - Interface segregation principle 接口分离原则 D - Dependency Inversion Principle 依赖反转原则

    面向对象设计OOD 面向对象设计OOD

    面向对象设计OOD 面向对象设计OOD 面向对象设计OOD 面向对象设计OOD

    如何解释OOD及设计

    看外国专家简单明了讲解如何解释OOD及设计

    OOD启思录 高清pdf

     全书共11章,总结出了60多条面向对象设计(OOD)的指导原则。这些经验原则涵盖了从类到对象(主要强调它们之间的关系,包括关联、使用、包含、单继承、多继承)到面向对象物理设计的重要主题。本书将帮助你理解经验...

    面向对象设计原则

    OOD六大设计原则及其内涵,用实例解析

    面向对象设计OOD思想

    面向对象设计(OOD)思想(示例代码见文末下载连接)收藏 有了思想才能飞翔,缺乏灵活就象少了轮子的汽车,难以飞奔。为了更好的理解设计思想,结合一个尽可能简洁的实例来说明OOD、设计模式及重构。通过下面的代码,...

    如何向妻子解释面向对象设计(OOD)

    如何向妻子解释面向对象设计(OOD)

    什么是OOA与OOD

    点来认识现实世界、设计问题的可行解,随之也就出现了许多OOA和OOD方法。但这些方法 还不很成熟,在OOA与OOD的边界划分上也存在着争议。如:有人认为面向对象软件开发 过程可以分为面向对象分析、面向对象设计和面向...

    OOD:OOD设计模式的实现

    OOD 总结各种设计模式应用的小项目

    如何向妻子解释OOD和设计模式——桥接模式

    包含两篇文档:如何向妻子解释OOD和如何向妻子解释设计模式。 ——国外程序员通过对话方式浅显易懂的描述。关于设计模式原文仅更新了一个桥接模式,so 并不是23种都有,但对思想有一定帮助。

    OOD启思录(高清)

    OOD设计很好的教材,高清 OOD启思录

    ATM_Java_源代码(OOA、OOD设计模式)

    面有两个帐号: 帐号:1 PIN:42 帐号:2 PIN:1234 原文是如下: To run the simulation above, you need to do the following: 1.Click on the "ON" button (lower right-hand corner) to turn the ATM on. ...

    面向对象的设计原则详解

    我们在进行面向对象设计(OOD)时应该怎样进行,遵循什么原则呢?我们或许听说过设计模式,那是针对特定的问题提出的特定的解决方法。面向对象的设计从提出到现在经过很多人的经验和实践,也总结出了很多原则。

    2018年最新 OOD面向对象设计专题班.zip

    2018年最新 OOD面向对象设计专题班2018年最新 OOD面向对象设计专题班2018年最新 OOD面向对象设计专题班2018年最新 OOD面向对象设计专题班

    OOD原则 GRASP GOF

    NULL 博文链接:https://wuaner.iteye.com/blog/587112

    面向对象设计(OOD)方法

    软件工程里的面向对象设计方法介绍

Global site tag (gtag.js) - Google Analytics