`

Liskov Substitution Principle (LSP) - OO设计的里氏替换原则

 
阅读更多

 


Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.


Robert C. Martin氏为我们总结了在面向对象的设计(OOD)中应该遵循的原则,这些原则被称为“Principles of OOD”,关于“Principles of OOD”的相关文章可以从Object Menter得到。

本文介绍“Principles of OOD”中的里氏替换原则:Liskov Substitution Principle (LSP)。

可以从这里查看Liskov Substitution Principle (LSP)的原文 

里氏替换原则LSP的概念解说
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象。也就是说,只有满足以下2个条件的OO设计才可被认为是满足了LSP原则
不应该在代码中出现if/else之类对子类类型进行判断的条件。以下代码就违反了LSP定义。
if (obj typeof Class1) {
    do something
} else if (obj typeof Class2) {
    do something else
}
子类应当可以替换父类并出现在父类能够出现的任何地方,或者说如果我们把代码中使用基类的地方用它的子类所代替,代码还能正常工作。

里氏替换原则LSP是使代码符合开闭原则的一个重要保证。同时LSP体现了:
- 类的继承原则:如果一个继承类的对象可能会在基类出现的地方出现运行错误,则该子类不应该从该基类继承,或者说,应该重新设计它们之间的关系。
- 动作正确性保证:从另一个侧面上保证了符合LSP设计原则的类的扩展不会给已有的系统引入新的错误。

类的继承原则:
Robert C. Martin氏在介绍Liskov Substitution Principle (LSP)的原文里,举了Rectangle和Square的例子。这里沿用这个例子,但用Java语言对其加以重写,并忽略了某些细节只列出下面的精要部分来说明 里氏替换原则 对类的继承上的约束。
代码:
class Rectangle {
    double width;
    double height;
    
    
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public double getWidth() {
        return width;
    }
    public void setWidth(double width) {
        this.width = width;
    }   
}

class Square extends Rectangle {
    public void setHeight(double height) {
        super.setHeight(height);
        super.setWidth(height);
    }
    
    public void setWidth(double width) {
        super.setHeight(width);
        super.setWidth(width);
    }
}


这里Rectangle是基类,Square从Rectangle继承。
这种继承关系有什么问题吗?

假如已有的系统中存在以下既有的业务逻辑代码:
void g(Rectangle r) {
    r.setWidth(5);
    r.setHeight(4);
    if (r.getWidth() * r.getHeight() != 20) {
        throw new RuntimeException();
    }
}

则对应于扩展类Square,在调用既有业务逻辑时:
        Rectangle square = new Square();
        g(square);
时会抛出一个RuntimeException异常。这显然违反了LSP原则。


动作正确性保证:
因为LSP对子类的约束,所以为已存在的类做扩展构造一个新的子类时,根据LSP的定义,不会给已有的系统引入新的错误。

Design by Contract
根据Bertrand Meyer氏提出的Design by Contract(DBC:基于合同的设计)概念的描述,对于类的一个方法,都有一个前提条件以及一个后续条件,前提条件说明方法接受什么样的参数数据等,只有前提条件得到满足时,这个方法才能被调用;同时后续条件用来说明这个方法完成时的状态,如果一个方法的执行会导致这个方法的后续条件不成立,那么这个方法也不应该正常返回。
现在把前提条件以及后续条件应用到继承子类中,子类方法应该满足:
1)前提条件不强于基类.
2)后续条件不弱于基类.
换句话说,通过基类的接口调用一个对象时,用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件,亦即,继承类方法必须接受任何基类方法能接受的任何条件(参数)。同样,继承类必须顺从基类的所有后续条件,亦即,继承类方法的行为和输出不得违反由基类建立起来的任何约束,不能让用户对继承类方法的输出感到困惑。
这样,我们就有了基于合同的LSP,基于合同的LSP是LSP的一种强化。

在很多情况下,在设计初期我们类之间的关系不是很明确,LSP则给了我们一个判断和设计类之间关系的基准:需不需要继承,以及怎样设计继承关系。
分享到:
评论

相关推荐

    OO设计原则-里氏替换原则

    其中,里氏替换原则(Liskov Substitution Principle, LSP)是一项非常重要的原则,它确保了子类可以替换其基类而不破坏程序的正确性。本文将深入探讨里氏替换原则的核心概念及其应用场景,帮助读者理解这一原则的...

    软件架构设计的6大原则.pdf

    3. 里氏替换原则(Liskov Substitution Principle - LSP) 里氏替换原则是指基类能够被子类替换,但子类不能替换基类。这个原则强调了继承关系的正确性,防止继承关系的混乱。应用时,需要在继承类时,务必重写父类...

    2020-review-3-oo.pptx

    - **里氏替换原则**(Liskov Substitution Principle, LSP):子类必须能够替换它们的基类。 - **依赖倒置原则**(Dependency Inversion Principle, DIP):高层模块不应该依赖低层模块,两者都应该依赖其抽象。 - *...

    java oo 设计原则

    4. 里氏替换原则(Liskov Substitution Principle, LSP) LSP要求子类必须能够替换其基类,而不影响程序的正确性。这意味着子类可以使用在基类出现的地方,且不应引入新的约束。这保证了多态性的有效利用,增强了代码...

    面向对象编程,OO设计的五大原则

    在本文中,我们将详细探讨OOP设计中的五大原则:单一职责原则(Single Responsibility Principle,简称 SRP)、开放封闭原则(Open-Closed Principle,简称 OCP)、里氏替换原则(Liskov Substitution Principle,...

    面向对象设计原则解析

    3. **里氏替换原则(Liskov Substitution Principle, LSP)** 如果子类型可以被替换为其基类型而不影响程序的正确性,那么就满足了里氏替换原则。这意味着子类必须能够完全替代其父类,并且所有依赖于父类型的引用...

    多媒体件类图PPT学习教案.pptx

    - **里氏替换原则(Liskov Substitution Principle, LSP)**:子类必须能够替换它们的父类,而不影响程序的正确性。子类不应添加父类没有的约束。 - **依赖倒转原则(Dependency Inversion Principle, DIP)**:...

    oo design principles

    3. 里氏替换原则(Liskov Substitution Principle, LSP) LSP,又称设计契约,是Barbara Liskov提出的。根据这个原则,子类型必须能够被其基类型无缝替换,而不会导致程序行为的错误。也就是说,如果一个程序对基...

    设计模式入门指导

    - 里氏替换原则(Liskov Substitution Principle, LSP):子类可以替换父类出现在父类能够出现的任何地方。 - 接口隔离原则(Interface Segregation Principle, ISP):不应该强迫客户依赖于它们不用的方法。 - 依赖...

    面向对象的设计原则 doc

    #### 三、里氏代换原则(Liskov Substitution Principle, LSP) **定义**:里氏代换原则指出,子类应该能够替换其基类,并且在不修改原有代码的情况下保持原有行为不变。 **实践意义**: - **避免破坏继承**:在...

    Head First Object-Oriented Design and Analysis

    3. **里氏替换原则**(Liskov Substitution Principle, LSP):子类型必须能够替换它们的基类型。 4. **依赖倒置原则**(Dependency Inversion Principle, DIP):高层模块不应该依赖低层模块,二者都应该依赖于抽象...

    实战OO 部署与实施

    3. **里氏替换原则**(Liskov Substitution Principle, LSP):子类型必须能够替换它们的基类型。 4. **依赖倒置原则**(Dependency Inversion Principle, DIP):高层模块不应该依赖于低层模块,二者都应该依赖于...

Global site tag (gtag.js) - Google Analytics