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

2019-01-13  本文已影响0人  a_salt_fish

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

引申意义:

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

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源码

上一篇下一篇

猜你喜欢

热点阅读