首页 > 编程笔记 > Java笔记

使用里氏替换原则解决实际问题

下面介绍一个经典的业务场景,用正方形、矩形和四边形的关系来说明里氏替换原则。

我们都知道正方形是一个特殊的长方形,首先创建一个长方形父类 Rectangle,代码如下。
public class Square extends Rectangle {
    private long length;

    public long getLength() {
        return length;
    }

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

    @Override
    public long getHeight() {
        return getLength();
    }

    @Override
    public void setHeight(long height) {
        setLength(height);
    }

    @Override
    public void setWidth(long width) {
        setLength(width);
    }

    @Override
    public long getWidth() {
        return getLength();
    }
}
在测试类中,创建 resize() 方法。根据逻辑,长方形的宽应该大于等于高,我们让高一直自增,直到高等于宽变成正方形,代码如下。
public static void resize(Rectangle rectangle) {
    while (rectangle.getWidth() >= rectangle.getHeight()) {
        rectangle.setHeight(rectangle.getHeight() + 1);
        System.out.println("width: " + rectangle.getWidth() +
                ",Height: " + rectangle.getHeight());
    }
    System.out.println("Resize End,width:" + rectangle.getWidth() +
            " ,Height:" + rectangle.getHeight());

}
客户端测试代码如下:
public static void main(String[] args) {
    Rectangle rectangle = new Rectangle();
    rectangle.setWidth(20);
    rectangle.setHeight(10);
    resize(rectangle);
}
运行结果如下:
width: 20,Height: 11
width: 20,Height: 12
width: 20,Height: 13
width: 20,Height: 14
width: 20,Height: 15
width: 20,Height: 16
width: 20,Height: 17
width: 20,Height: 18
width: 20,Height: 19
width: 20,Height: 20
width: 20,Height: 21
Resize End,width:20 ,Height:21
由运行结果可知,高比宽还大,这在长方形中是一种非常正常的情况。再看下面的代码,把长方形替换成它的子类正方形,修改客户端测试代码如下:
public static void main(String[] args) {   
    Square square = new Square();
    square.setLength(10);
    resize(square);
}
此时,运行出现了死循环,违背了里氏替换原则,在将父类替换成子类后,程序运行结果没有达到预期。因此,代码设计是存在一定风险的。

里氏替换原则只存在于父类和子类之间,约束继承泛滥。再来创建一个基于长方形与正方形共同的抽象——四边形 QuardRangle 接口,代码如下。
public interface QuardRangle {
    long getWidth();
    long getHeight();
}
修改长方形 Rectangle 类的代码如下。
public class Rectangle implements QuardRangle{
    private long height;    //高
    private long width;    //宽

    public long getHeight() {
        return height;
    }

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

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

    public long getWidth() {
        return width;
    }
}
修改正方形 Square 类的代码如下。
public class Square implements QuardRangle {
    private long length;

    public long getLength() {
        return length;
    }

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

    @Override
    public long getHeight() {
        return getLength();
    }

    @Override
    public long getWidth() {
        return getLength();
    }
}
此时,如果把 resize() 方法的参数换成四边形 QuardRangle 类,方法内部就会报错。因为正方形已经没有了 setWidth() 和 setHeight() 方法,所以,为了约束继承泛滥,resize() 方法的参数只能用长方形 Rectangle 类。

拓展

在讲开闭原则的时候,我们埋下了一个伏笔。在 JavaDiscountCourse 类中获取折扣价格时重写了父类的 getPrice() 方法,增加了一个获取源码的 getOriginPrice() 方法,这明显违背了里氏替换原则。

下面修改代码,增加 getDiscountPrice() 方法。JavaDiscountCourse 类代码如下:
public class JavaDiscountCourse extends JavaCourse {

    public JavaDiscountCourse(Integer id, String name, Double price) {
        super(id, name, price);
    }

    public Double getDiscountPrice() {
        return super.getPrice() * 0.8;
    }
}

所有教程

优秀文章