【设计模式】八、面向对象设计原则之合成复用原则

57 阅读3分钟

这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情

系列文章|源码

github.com/tyronczt/de…

定义-是什么

合成复用原则(Composite/Aggregate Reuse Principle,CARP)是指尽量使用对象组合/聚合而不是继承关系达到软件复用的目的。

它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则 与 里氏替换原则 相辅相成的,两者都是开闭原则的具体实现规范。

  • 聚合 has-A:整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享
  • 组合 contains-A :体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束
  • 继承 is-A :一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能

合成复用原则-说明.jpg

思考-为什么

合成/聚合复用

优点

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能

  • 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用;
  • 对象间的耦合度低。可以在类的成员位置声明抽象;
  • 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象;

缺点

  • 通过这种方式复用建造的系统会有较多的对象需要管理;

继承复用

优点

  • 新的实现较为容易,因为基类的大部分功能可以通过继承关系自动进入派生类;
  • 修改或扩展继承而来的实现较为容易。

缺点

  • 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用;
  • 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护;
  • 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化;

应用-怎么用

案例:汽车分类管理程序

仓库代码地址:github.com/tyronczt/de…

汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。

public class CarMaker {

    public static void main(String[] args) {
        RedGasolineCar c1 = new RedGasolineCar();
        c1.move();
    }
}

class GasolineCar {
    public void move() {
        System.out.println("gasoline move");
    }
}

class ElectricCar {
    public void move() {
        System.out.println("electric move");
    }
}

class RedGasolineCar extends GasolineCar {
    public void move() {
        System.out.println("red gasoline move");
    }
}

class BlueGasolineCar extends GasolineCar {
    public void move() {
        System.out.println("blue gasoline move");
    }
}

合成复用-原始代码.png 从上面实现中我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类。我们试着将继承复用改为聚合复用看一下。

public class CarMaker2 {
    public static void main(String[] args) {
        Car c = new Car(new Gasoline(), new Blue());
        c.move();
    }
}

class Car {
    private Energy energy;
    private Color color;

    public Car(Energy e, Color c) {
        this.energy = e;
        this.color = c;
    }

    public void move() {
        System.out.println(energy.toString() + color.toString() + "move");
    }
}

interface Energy {
}

class Gasoline implements Energy {
    public String toString() {
        return "gasoline";
    }
}

class Electric implements Energy {
    public String toString() {
        return "electric";
    }
}

interface Color {
}

class Red implements Color {
    public String toString() {
        return "red";
    }
}

class Blue implements Color {
    public String toString() {
        return "blue";
    }
}

合成复用-修改代码.png

参考

合成复用原则是什么? 聚合,组合,继承你还傻傻分不清楚?【设计模式系列6】_哔哩哔哩_bilibili

软件设计原则 - 掘金