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