架构整洁之道笔记(四)-组件构建原则

第十二章 组件

组件是软件的部署单元,是整个软件系统在部署过程中可以独立完成部署的最小实体。比如,对于Java来说,它的组件是jar文件。
我们可以将多个组件链接成一个独立可执行文件,也可以将它们汇总成类似.war文件这样的部署单元。
我们常常会在程序运行时插入某些动态链接文件,这些动态链接文件所使用的就是软件架构中的组件概念。在经历了50年的演讲之后,组件化的插件式架构已经成为我们习以为常的软件架构形式了。

第十三章 组件聚合

究竟就哪些类应该被组合成一个组件呢?
有三个与构建组件相关的基本原则。
1. REP:复用/发布等同原则。
软件复用的最小粒度应等同于其发布的最小粒度。
从软件设计和架构设计的角度来看,REP原则就是指组件中的类与模块必须是彼此紧密相关的。也就是说,一个组件不能有一组毫无关联的类和模块组成,它们之间应该有一个共同的主题或者大方向。
一个组件中包含的类与模块还应该是可以同时发布的。这意味着它们共享相同的版本号与版本跟踪,并且包含在相同的发行文档中,这些都应该同时对该组件的作者和用户有意义。

2. CCP:共同闭包原则。

我们应该将那些会同时修改,并且为相同目的而修改的类放到同一组件中。而将不会同时修改,并且不会为了相同目的而修改的那些类放到不同的组件中。
正如SRP原则中提到的一个类不应该同时存在多个变更原因一样,CCP原则也认为一个组件不应该同时存在多个变更原因。CCP原则实际上就是SRP原则的组件版。

3. CRP:共同复用原则。
不要强迫一个组件的用户依赖他们不需要的东西。
不是紧密相连的类不应该被放在同一个组件里。
CRP原则实际上是ISP原则的一个普适版。ISP原则是建议我们不要依赖带有不需要的函数的类,而CRP原则则是建议我们不要依赖带有不需要的类的组件。

第十四章 组件耦合

ADP 无依赖环原则:组件依赖关系图中不应该出现环

循环依赖示例:

架构整洁之道笔记(四)-组件构建原则

打破循环依赖的两种机制:
1.应用依赖反转原则(DIP):

架构整洁之道笔记(四)-组件构建原则

 创建一个User类需要使用的接口,然后将这个接口放入Entities组件,并在Authorizer组件中继承它。将Entities和Authorizer之间的依赖关系反转,打破了循环依赖关系。

2.创建一个新的组件,并让Entities与Authorize这两个组件都依赖于它。将现有的这两个组件中互相依赖的类全部放入新组件

 架构整洁之道笔记(四)-组件构建原则

SDP 稳定依赖原则: 依赖关系必须要指向更稳定的方向。

架构整洁之道笔记(四)-组件构建原则

 X是一个稳定的组件,因为有三个组件依赖着X,所以X有三个不应该被修改的原因。X不依赖于任何组件,所以不会有任何原因导致它需要被变更,我们称它为“独立”组件。

架构整洁之道笔记(四)-组件构建原则

Y是一个非常不稳定的组件。由于没有其他的组件依赖Y,所以Y并不对任何组件负责。但因为Y同时依赖于三个组件,所以它的变更就可能由三个不同的源产生。Y是有依赖性的组件。 

如何量化一个组件的稳定性?

架构整洁之道笔记(四)-组件构建原则

违反SDP的例子:

架构整洁之道笔记(四)-组件构建原则

 Stable组件中的U使用了Flexible组件中的C。

架构整洁之道笔记(四)-组件构建原则

 利用DIP来修复这个问题。

 SAP 稳定抽象原则: 一个组件的抽象化程度应该与其稳定性保存一致。

稳定抽象原则为组件的稳定性与它的抽象化程度建立了一种关联。一方面,该原则要求稳定的组件同时应该是抽象的,这样它的稳定性就不会影响到扩展性。另一方面,该原则也要求一个不稳定的组件应该包含具体的实现代码,这样它的不稳定性就可以通过具体的代码被轻易修改。

因此,如果一个组件想要成为稳定组件,那么它就应该由接口和抽象类组成,以便将来做扩展

将SAP与SDP这两个原则结合起来,就等于组件层次上的DIP。因为SDP要求的是让依赖关系指向更稳定的方向,而SAP则告诉我们稳定性本身就隐含了对抽象化的要求,即依赖关系应该指向更抽象的方向。

组件是允许部分抽象,部分稳定的。
衡量抽象化程度的指标:

架构整洁之道笔记(四)-组件构建原则

 组件的稳定性I与抽象化程度A的关系图:

架构整洁之道笔记(四)-组件构建原则

痛苦区:
(0,0)周围的这个区域称为痛苦区。在这个区域的组件是非常稳定但也非常具体的组件。这样的组件在设计上是不佳的,因为它很难被修改,这意味着该组件不能被扩展。典型案例是数据库的表结构,它在可变性上可谓臭名昭著,但是它同时又非常具体,并被非常多的组件依赖。另一个典型软件组件是工具型类库。
无用区:
靠近(1,1)的这个区域称为无用区。这个区域的组件通常是无限抽象的,但是没有被其他组件依赖,这样的组件往往无法使用。例如我们常常会在系统的某个角落里看到某个没有人实现的抽象类,它们一直静静地躺在那里,没有人使用。

主序列线:
从(1,0)连接到(0,1)的这条线称为主序列线。
主序列线上的组件不会为了追求稳定性而被设计得太过抽象,也不会为了避免抽象化而被设计得太过不稳定。这样的组件既不会特别难以被修改,又可以实现足够的功能。
在整条主序列线上,组件所能处于最优的位置是线的两端。一个优秀的软件架构师应该争取将自己设计的大部分组件尽可能地推向这两个位置。