6、里氏替换原则(Liskov Substitution Principle)

concept

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象


analyse

  1. 里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法,因为这样可能会改变父类原有的行为。
  2. 任何基类可以出现的地方,子类一定可以出现,也就是说,如果我们把代码中使用基类的地方用它的子类所代替,代码还能正常工作。
  3. 子类必须完全实现父类的方法,如果子类不能完整地实现父类的方法,或者子类出现在基类出现的地方发生运行错误,应该重新设计它们之间的关系,如:父类和子类都继承一个更通用的基类、原有的继承关系去掉,采用依赖、聚合、组合等关系代替…

example

这里列举最经典的一个例子正方形 【正方形是矩形的一种特殊情况,即四边相等的矩形】 和矩形 【有一个角是直角的平行四边形叫做矩】

/**
 * 矩形有两个属性,长(length)和宽(breadth)
 */
class Rectangle {
    private int length;
    private int breadth;

    public int getLength() { return length;}

    public void setLength(int length) {this.length = length;}

    public int getBreadth() {return breadth;}

    public void setBreadth(int breadth) {this.breadth = breadth;}

    public int getArea() {return this.length * this.breadth;}
}

因为在数理中,正方形是一种特殊的矩形且具有矩形全部特性,所以正方形Square继承矩形Rectangle,而正方形Square的四边都相等,所以我们重写了父类RectanglesetLengthsetBreadth方法,代码如下

/**
 * 正方形
 */
class Square extends Rectangle {
    @Override
    public void setBreadth(int breadth) {
        super.setLength(breadth);
        super.setBreadth(breadth);
    }
    @Override
    public void setLength(int length) {
        super.setLength(length);
        super.setBreadth(length);
    }
}

现在,无论谁设置 Square 对象的 Width,它的 Height 也会相应跟着变化。而当设置 Height 时,Width 也同样会改变。这样做之后,Square 对象仍然是一个看起来很合理的数学中的正方形。

public class Client{
    public static void main (String args[]) {
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setBreadth(5);
        System.out.println(rectangle.getClass().getSimpleName()+" area is" + rectangle.getArea());
    }
}

输出结果为 Rectangle area is 50

根据定义 【所有引用基类的地方必须能透明地使用其子类的对象】,这里将基类Rectangle 替换为子类Square

public class Client{
    public static void main (String args[]) {
        Rectangle rectangle = new Square();
        rectangle.setLength(10);
        rectangle.setBreadth(5);
        System.out.println(rectangle.getClass().getSimpleName()+" area is" + rectangle.getArea());
    }
}

输出结果为 Rectangle area is 25

所以这种情况不满足里氏替换原则,显然在java所构建的世界中,现实世界的理论有时候是行不通的.这里断开SquareRectangle的联系,重新构建一个Square独有的对象,实现如下

class Square {

    private int length;
  
    public int getLength() { return length;}

    public void setLength(int length) {this.length = length;}
}

public class Client{
    public static void main (String args[]) {
        Square square = new Square();
        square.setLength(10);
        square.setBreadth(5);
        System.out.println(square.getClass().getSimpleName()+" area is" + square.getArea());
    }
}

契约式设计(Design by Contract)

Bertrand Meyer 在 1988 年阐述了 LSP 原则与契约式设计之间的关系

对于类的一个方法,都有一个前提条件以及一个后续条件

  • 前提条件说明方法接受什么样的参数数据等,只有前提条件得到满足时,这个方法才能被调用;
  • 后续条件用来说明这个方法完成时的状态,如果一个方法的执行会导致这个方法的后续条件不成立,那么这个方法也不应该正常返回。

现在把前提条件以及后续条件应用到继承子类中,子类方法应该满足:

  1. 前提条件不强于基类
  2. 后续条件不弱于基类.

开放封闭原则(Open Closed Principle)是许多面向对象设计启示思想的核心。符合该原则的应用程序在可维护性、可重用性和鲁棒性等方面会表现的更好。里氏替换原则(Liskov Substitution Principle)则是实现 OCP 原则的重要方式。只有当衍生类能够完全替代它们的基类时,使用基类的函数才能够被安全的重用,然后衍生类也可以被放心的修改了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值