美文网首页
设计原则(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) : 替换原则

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