里氏替换准则(Liskov Substitution Principle,LSP)是指如果对每一个类型为T1的对象o1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

这个定义看上去还是比拟形象的,咱们从新了解一下。能够了解为一个软件实体如果实用于一个父类,那么肯定实用于其子类,所有援用父类的中央必须能通明地应用其子类的对象,子类对象可能替换父类对象,而程序逻辑不变。依据这个了解,引申含意为:子类能够扩大父类的性能,但不能扭转父类原有的性能。

(1)子类能够实现父类的形象办法,但不能笼罩父类的非形象办法。

(2)子类能够减少本人特有的办法。

(3)当子类的办法重载父类的办法时,办法的前置条件(即办法的输出/入参)要比父类办法的输出参数更宽松。

(4)当子类的办法实现父类的办法时(重写/重载或实现形象办法),办法的后置条件(即办法的输入/返回值)要比父类更严格或与父类一样。

在讲开闭准则的时候我埋下了一个伏笔,在获取折扣时重写笼罩了父类的getPrice()办法,减少了一个获取源码的办法getOriginPrice(),显然就违反了里氏替换准则。咱们批改一下代码,不应该笼罩getPrice()办法,减少getDiscountPrice()办法:

public class JavaDiscountCourse extends JavaCourse {    public JavaDiscountCourse(Integer id, String name, Double price) {        super(id, name, price);    }    public Double getDiscountPrice(){        return super.getPrice() * 0.61;    }}

应用里氏替换准则有以下长处:

(1)束缚继承泛滥,是开闭准则的一种体现。

(2)增强程序的健壮性,同时变更时也能够做到十分好的兼容性,进步程序的可维护性和扩展性,升高需要变更时引入的危险。

当初来形容一个经典的业务场景,用正方形、矩形和四边形的关系阐明里氏替换准则,咱们都晓得正方形是一个非凡的长方形,所以就能够创立一个父类Rectangle:

public class Rectangle {    private long height;    private long width;    @Override    public long getWidth() {        return width;    }    @Override    public long getLength() {        return length;    }    public void setLength(long length) {        this.length = length;    }    public void setWidth(long width) {        this.width = width;    }}

创立正方形类Square继承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 getWidth() {        return getLength();    }    @Override    public long getHeight() {        return getLength();    }    @Override    public void setHeight(long height) {        setLength(height);    }    @Override    public void setWidth(long width) {        setLength(width);    }}

在测试类中创立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办法完结" +            "\nwidth:"+rectangle.getWidth() + ",height:"+rectangle.getHeight());}

测试代码如下:

public static void main(String[] args) {    Rectangle rectangle = new Rectangle();    rectangle.setWidth(20);    rectangle.setHeight(10);    resize(rectangle);}

运行后果如下图所示。

咱们发现高比宽还大了,这在长方形中是一种十分失常的状况。当初咱们把Rectangle类替换成它的子类Square,批改测试代码:

public static void main(String[] args) {    Square square = new Square();    square.setLength(10);    resize(square);}

上述代码运行时呈现了死循环,违反了里氏替换准则,将父类替换为子类后,程序运行后果没有达到预期。因而,咱们的代码设计是存在肯定危险的。里氏替换准则只存在于父类与子类之间,束缚继承泛滥。咱们再来创立一个基于长方形与正方形独特的形象四边形接口Quadrangle:

public interface Quadrangle {    long getWidth();    long getHeight();}

批改长方形类Rectangle:

public class Rectangle implements Quadrangle {    private long height;    private long width;    @Override    public long getWidth() {        return width;    }    public long getHeight() {        return height;    }    public void setHeight(long height) {        this.height = height;    }    public void setWidth(long width) {        this.width = width;    }}

批改正方形类Square:

public class Square implements Quadrangle {    private long length;    public long getLength() {        return length;    }    public void setLength(long length) {        this.length = length;    }    @Override    public long getWidth() {        return length;    }    @Override    public long getHeight() {        return length;    }}

此时,如果咱们把resize()办法的参数换成四边形接口Quadrangle,办法外部就会报错。因为正方形类Square曾经没有了setWidth()和setHeight()办法。因而,为了束缚继承泛滥,resize()办法的参数只能用Rectangle类。当然,咱们在前面的设计模式的内容中还会持续深刻解说。

小测一下

本文为原创文章,转载请注明出处!关注微信公众号“Tom弹架构”,回复“材料”、“简历”、“刷题”,“招聘”即可支付面试真题,简历模板等!