Skip to content
On this page

里氏替换原则

  • 里氏替换原则是面向对象涉及的基本原则之一
  • 里氏替换原则:任务基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类原有的功能。换句话说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
  • 如果通过重写父类的方法来完成新的功能,这样写起来很简单,但是整个继承计息的可复用性会比较差,特别是运用多态比较频繁时,程序出错的概率非常大

例:

在数学理念关于中,正方式属于长方形,即长宽相等的特殊长方形。所以,我们开发的一个与几何图形相关的团建系统,就可以顺利成章的让正方形继承长方形

原始方案

长方形

java
package com.huangjiliang.design.heima.principle.demo2;

import lombok.Data;

/**
 * 长方形实体类
 */
@Data
public class Rectangle {

    /**
     * 长度
     */
    private Long length;

    /**
     * 宽度
     */
    private Long width;

}

长方形

java
package com.huangjiliang.design.heima.principle.demo2;

import lombok.Getter;

/**
 * 正方形实体类,继承长方形
 */
@Getter
public class Square1 extends Rectangle{

    @Override
    public void setLength(Long length) {
        super.setLength(length);
        super.setWidth(length);
    }

    @Override
    public void setWidth(Long width) {
        super.setWidth(width);
        super.setLength(width);
    }
}

演示主类

java
package com.huangjiliang.design.heima.principle.demo2;

public class RectangleDemo {

    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    public static void printLengthAndWidth(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }

    public static void main(String[] args) {

        // 长方形
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(40L);
        rectangle.setWidth(30L);
        resize(rectangle);
        printLengthAndWidth(rectangle);

        // 正方形
        Square1 square1 = new Square1();
        square1.setLength(30L);
        resize(square1);
        printLengthAndWidth(square1);

    }
}

UML图

image-20220911131622378

分析

运行以上main方法时候,发现程序一致在运行,直到内存溢出报错

  1. resize方法传入普通的长方形实体类,当宽度小于长度时候,进入while循环,宽度+1,当宽度大于长度之后,退出循环,可以得到预期效果
  2. resize方法传入特殊情况的长方形实体类(即正方形),因为正方形长款时相等的,会一直在while死循环,无法推出,导致内存溢出
  3. 所以:只有普通的长方形是适合resize这个方法,其子类square不适合。即Rectangle类型的参数是不能Square类型的参数所替代,即进行了替换得不到预期的结果。因此Square和Rectangle类之间的继承关系违反了里氏替换原则,他们之间的继承关系不成立,正方形不是长方形

改进方案

抽象一个四边形接口(Quadrilateral),让Rectangle和Square类实现Quadrilateral接口

四边形

java
package com.huangjiliang.design.heima.principle.demo2.improve;

public interface Quadrilateral {

    Long getLength();

    Long getWidth();
}

长方形

java
package com.huangjiliang.design.heima.principle.demo2.improve;

/**
 * 长方形实体类,实现四边形
 */
public class Rectangle implements Quadrilateral {

    /**
     * 长度
     */
    private Long length;

    /**
     * 宽度
     */
    private Long width;

    @Override
    public Long getLength() {
        return length;
    }

    public void setLength(Long length) {
        this.length = length;
    }

    @Override
    public Long getWidth() {
        return width;
    }

    public void setWidth(Long width) {
        this.width = width;
    }
}

正方形

java
package com.huangjiliang.design.heima.principle.demo2.improve;

import lombok.Data;

/**
 * 正方形实体类,实现四边形
 */
@Data
public class Square implements Quadrilateral {

    private Long size;

    @Override
    public Long getLength() {
        return size;
    }

    @Override
    public Long getWidth() {
        return size;
    }
}

演示类

java
package com.huangjiliang.design.heima.principle.demo2.improve;

/**
 * 演示里氏替换原则原始方式【正方形继承长方形违反里氏替换原则】
 */
public class RectangleDemo {

    /**
     * resize方法只适合普通的长方形,不能传入特殊四边形的正方形
     * @param rectangle
     */
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    public static void printLengthAndWidth(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }

    public static void main(String[] args) {

        // 长方形
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(40L);
        rectangle.setWidth(30L);
        resize(rectangle);
        printLengthAndWidth(rectangle);

    }
}

UML图

image-20220911134309491

分析

  1. 改进后的方案的resize方法只适合普通长方形,不适合特殊四边形的正方形