美文网首页
设计原则(6) : 替换原则

设计原则(6) : 替换原则

作者: a_salt_fish | 来源:发表于2019-01-13 20:35 被阅读0次

如果对每一个类型为T1的对象o1, 都有类型为T2的对象o2, 使得以T1定义的所有程序P在所有的对象o1都替换成o2时, 程序P的行为都没有发生变化, 那么类型T2是类型T1的子类型. (简单来说就是父类能正常运行的地方, 这个父类的任意子类都能正常运行,且程序逻辑保持不变)

引申意义:
  • 子类可以扩展父类的功能,但不能改变原有的功能
    含义1: 子类可以实现父类的抽象, 但不能覆盖父类的非抽象(重写).
    含义2: 子类可以增加自己特有的方法
    含义3: 当子类方法重载父类方法时, 方法的前置条件(入参)要比父类方法的更宽松
    含义4: 子类实现父类方法时(重写/重载或实现抽象方法), 方法的后置条件(返回值)要比父类更严格或相等(父类返回Map,子类返回Map或Map的实现类)

不遵循替换原则的代码实例:
正方形与长方形(正方形是一种特殊的长方形)
长方形

public class Rectangle1{

    private long length;
    private long width;
  
    public Rectangle1() {
    }

    public Rectangle1(long length, long width) {
        if(width > length){
            throw new IllegalArgumentException("宽度不能大于长度!");
        }
        this.length = length;
        this.width = width;
    }

    public long getLength() {
        return length;
    }

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

    public long getWidth() {
        return width;
    }

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

正方形继承长方形, 并重写get set方法

public class Square1 extends Rectangle1 {

    private long sideLength;


    public Square1(long sideLength) {
        this.sideLength = sideLength;
    }

    @Override
    public long getLength() {
        return sideLength;
    }

    @Override
    public void setLength(long length) {
       this.sideLength = length;
    }

    @Override
    public long getWidth() {
        return sideLength;
    }

    @Override
    public void setWidth(long width) {
        this.sideLength = width;
    }
}

定义一个方法,传入一个长方体,将这个长方体的长度调整为宽度的2倍

public class Test1 {
    /**
     * 调整长度为宽度的2倍
     * @param rectangle
     */
    public static void resize(Rectangle1 rectangle){
        rectangle.setLength(rectangle.getWidth() * 2);
        System.out.println("resize方法结束 width:"+ rectangle.getWidth() + " length:"+ rectangle.getLength());
    }
    public static void main(String[] args) {
        Rectangle1 rectangle = new Rectangle1(10, 8);
        resize(rectangle);
        Rectangle1 square1 = new Square1(10);
        resize(square1);
    }
}

执行结果为

resize方法结束 width:8 length:16
resize方法结束 width:20 length:20

很显然, 在resize这个方法中 正方体作为长方体的子类并没有得到我们预期的结果, 违背了替换原则, 在这个需求中, 正方体是不能作为长方体的子类的, resize 这个函数并不能接收一个正方体的函数


重新设计正方体与长方体的关系
抽象出一个四边形

public interface Quadrangle {
    long getWidth();
    long getLength();
}

长方体与正方体分别实现接口

public class Rectangle2 implements Quadrangle {
    private long length;
    private long width;

    public Rectangle2(long length, long width) {
        if(width > length){
            throw new IllegalArgumentException("宽度不能大于长度!");
        }
        this.length = length;
        this.width = 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;
    }
}

public class Square2 implements Quadrangle {

    private long sideLength;

    public Square2(long sideLength) {
        this.sideLength = sideLength;
    }

    public long getSideLength() {
        return sideLength;
    }

    public void setSideLength(long sideLength) {
        this.sideLength = sideLength;
    }

    @Override
    public long getWidth() {
        return sideLength;
    }

    @Override
    public long getLength() {
        return sideLength;
    }
}

测试

public class Test2 {
    /**
     * 调整长度为宽度的2倍
     * @param rectangle
     */
    public static void resize(Rectangle2 rectangle){
        rectangle.setLength(rectangle.getWidth() * 2);
        System.out.println("resize方法结束 width:"+ rectangle.getWidth() + " length:"+ rectangle.getLength());
    }
    public static void main(String[] args) {
        Rectangle2 rectangle = new Rectangle2(10, 8);
        resize(rectangle);
        Square2 square = new Square2(10);
        // 编译报错
        // resize(square);
    }
}

在编译阶段就避免错误的传入一个正方形进入resize方法


优点
  • 约束继承泛滥, 开闭原则的一种体现
  • 提高程序健壮性, 降低需求变更时可能引入的风险.

github源码

相关文章

  • 读《设计模式之禅》(二)

    上次看了设计模式的6大设计原则,单一职责原则、开闭原则、迪米特法则、里氏替换原则、接口隔离原则、依赖倒置原则。这次...

  • 设计原则(6) : 替换原则

    如果对每一个类型为T1的对象o1, 都有类型为T2的对象o2, 使得以T1定义的所有程序P在所有的对象o1都替换成...

  • 6大设计原则

    Python6大设计原则 阅读目录 内容总览 六大设计原则都有哪些 一、单一职责原则 二、里氏替换原则 三、依赖倒...

  • 六大设计原则

    6大设计原则总结 目录 一、单一职责原则 二、里氏替换原则 三、依赖倒置原则 四、接口隔离原则 五、迪米特法则 六...

  • 设计模式的设计原则

    设计模式的设计原则 面向对象五大设计原则 单一职责原则 里氏替换原则 依赖倒置原则 接口隔离原则 开闭原则 设计模...

  • 六大设计原则-里式替换原则【Liskov Substitutio

    六大设计原则 单一职责原则 里式替换原则 依赖导致原则 接口隔离原则 迪米特原则 开闭原则 里式替换原则 定义: ...

  • 设计模式之里氏替换原则

    设计模式之里氏替换原则 里氏替换原则(LSP: Liskov Substitution Principle) 定义...

  • 里氏替换原则

    个人博客原文:里氏替换原则 设计模式六大原则之二:里氏替换原则。 简介 姓名 :里氏替换原则 英文名 :Lisko...

  • 01-设计模式原则

    面向对象的设计原则 面向对象的设计原则也被称为SOLID。SOLID原则包括单一职责原则、开闭原则、里氏替换原则、...

  • 程序设计的6大原则

    程序设计的6大原则: 单一职责原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则开闭原则 从根本学好,理解为什么要...

网友评论

      本文标题:设计原则(6) : 替换原则

      本文链接:https://www.haomeiwen.com/subject/uvpldqtx.html