从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则

Posted 李阿昀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则相关的知识,希望对你有一定的参考价值。

在本讲,我将为大家介绍软件设计原则里面的第六个原则,即合成复用原则。

概述

合成复用原则是指尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

我们之前讲过继承关系,虽然使用继承可以提高代码的复用性,但是它存在很多问题,这些问题等会我们再去说,大家也别着急。

了解了合成复用原则的概念之后,我们还应知道,通常类的复用可分为继承复用(使用继承实现代码的复用性)和合成复用(使用组合或者聚合实现代码的复用性)两种。而且,这两种复用各有其优缺点,下面我就为大家详细阐述一下。

我们先来看下继承复用的优缺点。

  • 优点:简单、易实现

  • 缺点:

    1. 继承复用破坏了类的封装性。为什么说破坏了类的封装性呢?因为继承会将父类的实现细节暴露给子类,也就是说子类可以直接去继承父类中的功能,这样,子类就可以将父类中的功能给覆盖掉了,所以,父类对子类是透明的。其实,这种复用我们又可称为"白箱"复用
    2. 子类与父类的耦合度高。为什么说耦合度高呢?因为我们之前就已经讲过了,继承它本身就比组合、聚合的耦合度高。这样,父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护
    3. 继承限制了复用的灵活性。如何去理解呢?从父类继承而来的实现是静态的,这是因为在编译时就已经定义好了,所以在运行时是不可能发生变化的。我们总不能说,在程序运行过程中,来解除子类和父类的继承关系吧!况且,这也是无法实现的,所以我们才说继承限制了复用的灵活性

接下来,我们再看下组合或聚合复用的优点。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,那么新对象就可以调用已有对象的功能了。所以,它相比于继承复用有以下优点:

  • 维护了类的封装性。为什么说维护了类的封装性呢?因为成员对象的内部细节是对新对象不可见的,也就是说新对象不知道成员对象里面的具体的实现,但是可以调用其功能,所以这种复用又被称为"黑箱"复用

  • 对象间的耦合度低。我们之前就讲过,组合或者聚合本身就比继承的耦合度低。当我们真正去使用组合或者聚合复用时,我们可以在类的成员位置声明抽象父类或者父接口,这样,我们就能动态地去传递该抽象类或者父接口的子类对象了

  • 复用的灵活性高。这种复用可以在运行时动态进行,也就是说如果我们要给成员变量进行赋值,那么我们就可以在程序运行的时候才对其进行赋值了。若成员位置声明的是抽象的类或者接口,则我们就可以传递该接口或者该类的子类对象了。

    总之,这种复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的对象

了解完合成复用原则的概念,以及继承复用、组合或者聚合复用各自的优缺点之后,我们来看一个案例,通过该案例,大家再去深入理解一下合成复用原则。

案例

这个案例是汽车分类管理程序。

案例分析

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

以上案例要是使用继承复用的话,那么设计出来的类图就应该是下面这个样子的了。

从以上类图中可以看到,首先定义了一个顶层父类,即汽车类,而该类下面又有两个子类,一个是汽油汽车类,一个是电动汽车类。汽油汽车类下面又有两个子类,一个是红颜色的汽油汽车类,一个是白颜色的汽油汽车类;电动汽车类也是一样,它也有两个子类,一个是红颜色的电动汽车类,一个是白颜色的电动汽车类。

不知你有没有从以上类图中看出,使用继承复用产生了很多子类。如果现在又有一个新的动力源了,例如光能汽车,那么我们是不是又要在以上类图中添加一个新的子类啊?而且,我们还得添加更多的子类,因为光能汽车又得按"颜色"来划分。

案例实现

在这儿,我就不再通过代码来实现以上案例了,因为很简单,不值得我这样做。你要是有兴趣,不妨自个来写一下。

案例改进

如何对以上案例进行改进呢?很简单,将继承复用改为合成复用。那么,重新设计出来的类图就应该是下面这个样子了。

首先,来看下以上类图中右边的部分,在这一部分,我们定义了一个颜色接口,而且该接口有两个子实现类,一个是Red类,一个是White类;在以上类图中左边的部分,我们定义了一个顶层父类,即汽车类,而且该类还聚合了Color接口,也就是说,到时候我们在去创建汽车对象的时候,就需要给它传递具体的颜色的子类对象了。此外,该汽车类还有两个子类,一个是汽油汽车类,一个是电动汽车类。

对比一下以上继承复用和合成复用的类图,如果要是现在多了一种动力源,例如光能汽车,那么使用继承复用该如何去做呢?使用合成复用又该如何去做呢?

如果要是使用继承复用,那么设计出来的类图就应该是下面这个样子的。

以上就是我们新添加一种动力源,使用继承复用实现的方式。

下面,咱们再来看一下使用合成复用实现的方式。如果要是使用合成复用,那么设计出来的类图就应该是下面这个样子的。

这时,你会发现,使用继承复用的话,我们要添加多个类,而使用合成复用的话,我们只需要添加一个类。通过对比,大家可以很明显地看到合成复用会更好一些。所以,我们说合成复用原则它就是尽可能的先使用组合或者聚合等关联关系来实现,如果不能使用它俩进行实现的话,那么我们才会去考虑使用继承关系来实现。

至此,合成复用原则我就给大家解释清楚了。

以上是关于从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学习Java设计模式 | 软件设计原则篇:里氏代换原则

从零开始学习Java设计模式 | 软件设计原则篇:里氏代换原则

从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则

从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则

从零开始学习Java设计模式 | 软件设计原则篇:依赖倒转原则

从零开始学习Java设计模式 | 软件设计原则篇:依赖倒转原则