六大设计原则-里氏替换原则

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)
原文地址:https://www.cnblogs.com/Zs-book1/p/12714021.html