七大设计原则的简单解释之里氏替换原则、合成复用原则

815 阅读4分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

一、Liskov Substitution Principle里氏替换原则

(一)、里氏替换原则定义

​ 里氏替换原则是指如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

​ 简单来说便是,继续必须确保超类所拥有的性质在子类中仍然成立。

子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。 子类中可以增加自己特有的方法。 当子类重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

(二)、里氏替换原则优点

​ (1)、里氏替换原则是实现开闭原则的重要方式之一。

​ (2)、它克服了继承中重写父类造成的可复用性变差的缺点。

​ (3)、它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。

(三)、案例代码

1、不符合里氏替换原则的情况

长方形的类:

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

    public long getWidth() {
        return width;
    }

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

    public long getHeight() {
        return height;
    }

    public void setHeight(long height) {
        this.height = height;
    }
}

正方形的类:继承了长方形,并且改变了长方形的方法

public class Squre extends Rectangle {
    private long length;

    public long getLength() {
        return length;
    }

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

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

    @Override
    public void setWidth(long width) {
        setLength(width);
    }

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

    @Override
    public void setHeight(long height) {
        setLength(height);
    }
}

错误情况:检查长方形的长是否大于宽,如果没有就需要加1直到长大于宽,但是因为正方形是长方形的子类,所以传正方形的类依然可以成立,但是方法执行是死循环因为长和宽永远相等。

public class Check {
    public void resize(Rectangle rectangle){
        while(rectangle.getWidth()>=rectangle.getHeight()){
            rectangle.setHeight(rectangle.getHeight()+1);
            System.out.println("长"+rectangle.getHeight()+"宽"+rectangle.getWidth());
        }
        System.out.println("长"+rectangle.getHeight()+"宽"+rectangle.getWidth());
    }

    public static void main(String[] args) {
        Squre squre = new Squre();
        squre.setLength(10);
        Check check = new Check();
        check.resize(squre);
    }
}

2、里氏替换原则的方法的前置条件的范围不同的区别

​ 当子类重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。可以自己通过写代码验证

​ 你会发现执行结果依然是父类的,但一旦你的前置条件比父类方法更严格就相当于是子类的方法,执行的方法不同。子类可以扩展方法但不得重写或覆盖已经实现的方法

​ 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。这个当你用代码实现的时候如果不满足此条件编译器都会报错。

public class Parent {
    public void method(HashMap hashMap){
        System.out.println("父类方法执行");
    }
}
public class Son extends Parent{
    public void method(Map hashMap) {
        System.out.println("子类方法执行");
    }

    public static void main(String[] args) {
        Son son = new Son();
        HashMap hashMap = new HashMap();
        son.method(hashMap);
    }
}

二、Composite&Aggregate Reuse Principle合成复用原则

(一)、合成复用原则定义

​ 合成复用原则是指尽量使用对象组合(has-a)/聚合(contanis-a),而不是继承关系达到软件复用的目的,可以使得系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

​ 继承称之为白箱复用,相当于把所有的细节都暴露给子类。组合/聚合称之为黑箱复用,对类之外的对象是无法获取到细节的。

(二)、合成复用原则优点

​ (1)、它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

​ (2)、新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

​ (3)、复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

(三)、案例代码

public interface DBconnect {
    String getConnect();
}
public class MysqlConnect implements DBconnect {
    public String getConnect() {
        return "mysql连接";
    }
}
public class ProductConnect {
    private DBconnect dBconnect ;

    public void setdBconnect(DBconnect dBconnect) {
        this.dBconnect = dBconnect;
    }

    public void addConnect(){
        System.out.println(dBconnect.getConnect());
    }

    public static void main(String[] args) {
        ProductConnect productConnect = new ProductConnect();
        productConnect.setdBconnect(new MysqlConnect());
        productConnect.addConnect();
    }
}