软件设计原则-里式替换原则

定义:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1替换为o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

里式替换原则是继承复用的基石,是实现抽象化的规范,只有子类可以替换父类,并且功能不受影响时,父类才能真正的被复用,而子类也能在父类的基础上增加新的功能,里式替换原则是对开闭原则的一个补充。

定义扩展:如果一个软件实体适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明的使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。

引申含义:子类可以扩展父类的功能,但不能改变父类原有的功能。

含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

含义2:子类可以增加自己特有的方法。

含义3:当子类的方法重载父类的方法时,方法的前置条件(输入/入参),要比父类方法的输入参数更加宽松

含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(输出/返回值)要比父类更加严格或相等

优点:约束继承泛滥,开闭原则的一种体现。

案例

此处我们以经典的长方形和正方形为例,假设正方形是一种特殊的长方形,我们模拟长方形的长,不断的减少,直至与宽相等,此时长方形变成了一个正方形,程序退出运行,V1版本的代码如下,

public class Rectangle {
    private long length;
    private long width;

    public long getWidth() {
        return width;
    }

    public long getLength() {
        return length;
    }

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

    public void setWidth(long width) {
        this.width = width;
    }
}
public class Square extends Rectangle {
    private long sideWidth;

    public long getSideWidth() {
        return sideWidth;
    }

    public void setSideWidth(long sideWidth) {
        this.sideWidth = sideWidth;
    }

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

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

    @Override
    public void setLength(long length) {
        setSideWidth(length);
    }

    @Override
    public void setWidth(long width) {
        setSideWidth(width);
    }
}
public class Test {
    public static void resize(Rectangle rectangle){
        while (rectangle.getWidth() <= rectangle.getLength()){
            rectangle.setWidth(rectangle.getWidth()+1);
            System.out.println("width:"+rectangle.getWidth() + " length:"+rectangle.getLength());
        }
        System.out.println("resize方法结束 width:"+rectangle.getWidth() + " length:"+rectangle.getLength());
    }
//    public static void main(String[] args) {
//        Rectangle rectangle = new Rectangle();
//        rectangle.setWidth(10);
//        rectangle.setLength(20);
//        resize(rectangle);
//    }

    public static void main(String[] args) {
        Square square = new Square();
        square.setLength(10);
        resize(square);
    }
}

当我们在Test类中的main方法使用长方形作为入参的时,程序运行正常,但是当使用正方形作为入参进行实验时,程序出现了死循环,因为正方形的长宽相等,while (rectangle.getWidth() <= rectangle.getLength()),一直满足这个循环,而rectangle.setWidth(rectangle.getWidth()+1)又同时将长宽进行了赋值,所以出现了堆栈溢出的情况。

所以上述案例中,违反了里式替换原则的设计。所以要打破长方形和正方形的依赖关系,可以在上层在抽象出一个四边形类,让长方形和正方形都实现顶层的抽象,V2的代码如下。

四边形接口

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

}
public class Rectangle implements Quadrangle {
    private long length;
    private long width;

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

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

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

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

public class Square implements Quadrangle {
    private long 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 Test {
    public static void resize(Rectangle rectangle){
        while (rectangle.getWidth() <= rectangle.getLength()){
            rectangle.setWidth(rectangle.getWidth()+1);
            System.out.println("width:"+rectangle.getWidth() + " length:"+rectangle.getLength());
        }
        System.out.println("resize方法结束 width:"+rectangle.getWidth() + " length:"+rectangle.getLength());
    }

    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setWidth(10);
        rectangle.setLength(20);
        resize(rectangle);
    }
//    public static void main(String[] args) {
//        Square square = new Square();
//        square.setLength(10);
//        resize(square);
//    }


}

 此时的resize方法的入参我们就进行了约束,只能传入Rectangle,而不能进行替换成Square,从而满足了业务的要求,约束了继承的泛滥。

补充

入参的约束

public class Base {
    public void method(HashMap map){
        System.out.println("父类被执行");
    }
}
public class Child extends Base {
    /**
     * 重写
     * @param map
     */
    @Override
    public void method(HashMap map) {
        System.out.println("子类HashMap入参方法被执行");
    }

    /**
     * 重载
     * @param map
     */
    public void method(Map map) {
        System.out.println("子类HashMap入参方法被执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Base child = new Child();
        HashMap hashMap = new HashMap();
        child.method(hashMap);
    }
}

执行的结果:子类HashMap入参方法被执行

假如将Child代码进行调整

public class Child extends Base {
    /**
     * 重写
     * @param map
     */
//    @Override
//    public void method(HashMap map) {
//        System.out.println("子类HashMap入参方法被执行");
//    }

    /**
     * 重载
     * @param map
     */
    public void method(Map map) {
        System.out.println("子类HashMap入参方法被执行");
    }
}

再次运行程序,结果:父类被执行 

对代码再次进行调整

public class Base {
    public void method(Map map){
        System.out.println("父类被执行");
    }
}
 */
public class Child extends Base {
    /**
     * 重写
     * @param map
     */
    @Override
    public void method(Map map) {
        System.out.println("子类Map入参方法被执行");
    }

    /**
     * 重载
     * @param map
     */
    public void method(HashMap map) {
        System.out.println("子类HashMap入参方法被执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Base child = new Child();
        HashMap hashMap = new HashMap();
        child.method(hashMap);
    }
}

程序运行结果: 子类HashMap入参方法被执行,这样的话就违反了里式替换替换原则,所以子类的入参要比父类更加宽松

返回值的约束

public abstract class Base {
    public abstract Map method();

}

public class Child extends Base {
    @Override
    public HashMap method() {
        HashMap hashMap = new HashMap();
        System.out.println("子类method被执行");
        hashMap.put("message","子类method被执行");
        return hashMap;
    }
}

public class Test {
    public static void main(String[] args) {
        Child child = new Child();
        System.out.println(child.method());

    }
}

子类的返回值可以是Map也可以是更加严格的HashMap,但是不能比Map更宽松,否则会出现编译错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值