乐趣区

关于java:软件架构设计原则之里氏替换原则


        里氏替换准则(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 弹架构”,回复“材料”、“简历”、“刷题”,“招聘”即可支付面试真题,简历模板等!

退出移动版