[设计模式] 浅谈里氏替换原则

2024 年 4 月 16 日 星期二
/ ,
19

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

里氏替换原则

原则说明: 里氏替换原则(Liskov Substitution principle)是对子类型的特别定义,派生类(子类)对象可以在程序中代替其基类(超类)对象。

为什么要遵守该原则

违反里氏替换原则会导致代码的复杂性增加,可读性降低,同时也会增加代码修改的难度。当一个子类不能完全替代其父类时,这就增加了出错的可能性。
按我自己遇到的场景去理解,我调用了一个计算面积的方法,而子类无法完全替代超类,导致一个Bug的产生。下面就是一个简单的案例。

用一个简单的例子说明

假定我们有两个类,一个是矩形,一个是正方形继承矩形。

public class Rectangle {
    private int width;
    private int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return this.width * this.height;
    }
}

public class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setWidth(height);
        super.setHeight(height);
    }
}

有个计算面积的方法。如果你传入一个矩形对象,这个函数会打印20,这是正确的。但是如果你传入一个正方形对象,由于正方形的宽度和高度总是相等的,所以无论你怎么设置,它们总是会被设置为相同的值,这个函数会打印16或25,这就是错误的。

public void calculateArea(Rectangle r) {
    r.setWidth(5);
    r.setHeight(4);
    System.out.println(r.getArea());
}

遵守里氏替换原则,因为正方形类不能完全替代矩形类。我们应该将正方形类从矩形类中分离出来,使它们成为两个独立的类:

public class Rectangle {
    private int width;
    private int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return this.width * this.height;
    }
}

public class Square {
    private int side;

    public void setSide(int side) {
        this.side = side;
    }

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

符合里氏替换原则的继承

如果子类可以完全替代父类的行为,那么这个继承就是符合里氏替换原则的。
例如,有一个动物类和一个狗类,动物类的职责是吃,狗类的职责也是吃,那么狗类就可以完全替代动物类的职责,这个继承就是符合里氏替换原则的。

public class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}

如何避免违反里氏替换原则

  1. 从行为出发来设计。在做抽象或设计时,不只是要从模型概念出发,还要从行为出发。
  2. 明确职责,确保子类可以完全替代父类的职责
  3. 契约设计,时刻保持派生类与基类的契约不被破坏。
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...