2.里氏替换原则(Liskov Substitution Principle)(LSP)
定义: 所有引用基类的地方必须能透明的使用子类对象
里氏替换原则:就是说当在程序中将一个对象替换成他的子类时,程序可以继续原有的行为,他察觉不出符类和子类的区别。但是反过来却不成立,如果一个程序使用的是一个子类的话,他不一定适用于父类。
以电脑举例:电脑有CPU,电脑就是程序实体,CPU就是它使用的基类,CPU又有子类IntelCpu。
public class Cpu {
public void work(){
System.out.println("CPU在工作");
}
}
public class IntelCpu extends Cpu {
@Override
public void work() {
System.out.println("英特尔CPU工作");
}
}
public class Computer {
private Cpu cpu;
public void run() {
this.cpu.work();
}
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
}
电脑依赖的是父类,此时将Cpu换成Intel类型的,电脑仍能正常工作,它察觉不到任何改变,这符合里氏替换原则。
而反过来,假设现在有一台电脑,它只能使用IntelCpu才能工作。
public class IntelCpuComputer {
private IntelCpu cpu;
public void run() {
this.cpu.work();
}
public IntelCpu getCpu() {
return cpu;
}
public void setCpu(IntelCpu cpu) {
this.cpu = cpu;
}
}
如果此时将IntelCpu替换成父类CPU,那么计算机就不能正常工作,因为它只有在使用IntelCpu时才能正常工作。
public static void main(String[] args) {
IntelCpuComputer computer = new IntelCpuComputer();
computer.setCpu(new Cpu());//报错
}
再比如说,我们都知道在数学上正方形是特殊的长方形,那么能不能说正方形就是长方形的子类呢?
我们用代码表示一下正方形和长方形:
/**
* 长方形
* @author ZhaoShuai
* @date Create in 2020/4/12
**/
public class Rectangle {
private Integer height;
private Integer width;
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
public Integer getWidth() {
return width;
}
public void setWidth(Integer width) {
this.width = width;
}
}
/**
* 正方形
* @author ZhaoShuai
* @date Create in 2020/4/12
**/
public class Square {
private Integer side;
public Integer getSide() {
return side;
}
public void setSide(Integer side) {
this.side = side;
}
}
我们都知道,父类是通过抽取子类的共性封装而成的,从上面代码中可以看出,长方形和正方形的结构式不同的,长方形有长和宽的属性,而正方形只有变长的属性。
因此,正方形不是长方形的子类,当然也可以强行来继承长方形作为父类,但是我们不是为了继承而继承,这种为了继承而继承的情况也是不属于里氏替换原则的。
package com.xiazhi.principle.lsp;
/**
* @author ZhaoShuai
* @date Create in 2020/4/12
**/
public class SquareExtendsRectangle extends Rectangle {
private Integer side;
@Override
public void setHeight(Integer height) {
this.setSide(height);
}
@Override
public void setWidth(Integer width) {
this.setSide(width);
}
@Override
public Integer getHeight() {
return this.getSide();
}
@Override
public Integer getWidth() {
return this.getSide();
}
public Integer getSide() {
return side;
}
public void setSide(Integer side) {
this.side = side;
}
}
可以看出,我们给正方形强行继承了长方形,然后我们写一个测试类:
public static void main(String[] args) {
System.out.println("============长方形==============");
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(20);
print(rectangle);
System.out.println("============正方形==============");
Rectangle square = new SquareExtendsRectangle();
square.setHeight(10);
print(square);
}
private static void print(Rectangle rectangle) {
System.out.println("高:"+rectangle.getHeight());
System.out.println("宽:" + rectangle.getWidth());
}
测试后我们发现好像能正常运行,此时我们再加一个方法:
public static void main(String[] args) {
System.out.println("============长方形==============");
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(20);
test(rectangle);
System.out.println("============正方形==============");
Rectangle square = new SquareExtendsRectangle();
square.setHeight(10);
test(square);
}
private static void print(Rectangle rectangle) {
System.out.println("高:"+rectangle.getHeight());
System.out.println("宽:" + rectangle.getWidth());
}
private static void test(Rectangle rectangle) {
while (rectangle.getWidth() >= rectangle.getHeight()) {
rectangle.setHeight(rectangle.getHeight()+1);
print(rectangle);
}
}
运行后可以发现, 长方形可以正常运行,而正方形却会出现死循环,因此这种是不符合里氏替换原则的。因为引用父类的地方无法使用子类对象。
在类的设计时,根据依赖倒置原则,要使用抽象类或接口,如果不能使用抽象类或者接口,那么就说明设计违背了里氏替换原则(LSP)