二、里氏替换原则(lsp)
里氏替换原则的英文缩写是:lsp(谐音梗去记忆~,懂得都懂)
1.概念
任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法(注意是尽量,而不是必须!!!)
2. 经典案例 -> 正方形不是长方形
我们学过初中数学的都应该知道,正方形是特殊的长方形。假如我们想要开发一款与几何图形的软件相关的系统,我们通常都是让正方形继承长方形。
代码展示:
/**
* 长方形类
*/
@Data
public class Rectangle {
/**
* 长
*/
private double length;
/**
* 宽
*/
private double width;
}
/**
* 正方形类
*/
public class Square extends Rectangle {
@Override
public void setLength(double length) {
super.setLength(length);
super.setWidth(length);
}
@Override
public void setWidth(double width) {
super.setLength(width);
super.setWidth(width);
}
}
public class RectangleDemo {
public static void main(String[] args) {
//创建一个长方形
Rectangle rectangle = new Rectangle();
rectangle.setLength(20);
rectangle.setWidth(10);
resize(rectangle);
print(rectangle);
System.out.println("=====================");
//创建一个正方形
Square square = new Square();
square.setLength(20);
//会发现一直执行扩宽操作
resize(square);
print(square);
}
/**
* 扩宽方法,如果宽小于长,则扩宽,知道宽比长大
*/
private static void resize(Rectangle rectangle) {
while (rectangle.getWidth() <= rectangle.getLength()) {
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
/**
* 打印长和宽
*/
private static void print(Rectangle rectangle) {
System.out.println(rectangle.getLength() + " , " + rectangle.getWidth());
}
}
我们发现这是一个死循环。
lsp原则要求我们:子类可以扩展父类的功能,但不能改变父类原有的功能。
正方形继承了父类,但是正方形并不适合长方形的resize方法,不满足lsp。
我们可以看到resize这个方法是长方形具备的,但是正方形不具备的,所以这不满足lsp,这不是一个合理的继承关系,当然了,你要非得继承也是可以的,直接在正方形的resize中设置,调用resize就抛异常,直接屏蔽了正方形中的resize方法,但是不合理。
因为这是长方形独有的,所以我们可以抽出去。
正方形和长方形都是四边形。所以我们可以定义一个四边形的接口
修改后的代码:
/**
* 四边形接口
*/
public interface Quadrilateral {
/**
* 获取长
*/
double getLength();
/**
* 获取宽
*/
double getWidth();
}
/**
* 长方形类
*/
@Data
public class Rectangle implements Quadrilateral {
private double length;
private double width;
@Override
public double getLength() {
return length;
}
@Override
public double getWidth() {
return width;
}
}
/**
* 正方形类
*/
@Data
public class Square implements Quadrilateral {
private double side;
@Override
public double getLength() {
return side;
}
@Override
public double getWidth() {
return side;
}
}
public class Test {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setLength(20);
rectangle.setWidth(10);
resize(rectangle);
print(rectangle);
System.out.println("=====================");
//Square square = new Square();
//square.setSide(20);
//resize(square);
}
/**
* 扩宽方法,此时就是长方形的了,正方形用不了这个接口
*/
private static void resize(Rectangle rectangle) {
while (rectangle.getWidth() <= rectangle.getLength()) {
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
/**
* 打印长和宽
*/
private static void print(Rectangle rectangle) {
System.out.println(rectangle.getLength() + " , " + rectangle.getWidth());
}
}