模块化是 OOD 主要概念的一部分,被定义为“系统组件由相对独立的组件或可以组合的部分组成的程度”。由于模块化与封装密切相关,因此可以说模块化是将封装的抽象映射到真实的物理模块的一种方式。
Grady Booch 给出了定义模块的两个目标。首先,使模块具有内聚性(共享数据结构、相似的类),并具有允许最小模块间耦合的接口。团队合作、安全性和文档是其他考虑因素。
Robert C. Martin 将组件定义为部署单元,即构成可部署为 Jar 文件或 DLL 文件的软件的最小实体。本书介绍了决定如何模块化软件的一些原则,包括可重用性的耦合和内聚规则。
由于模块化和封装紧密相连,绘制模块边界的核心问题是“哪些类属于哪些组件或模块?” 这个问题的答案应该基于组件的内聚原则。正如《清洁架构》中所说,这些原则是:
一个组件不能仅仅由一堆随机选择的类组成,而它们之间没有合理的关系。类应该属于一个可以一起发布的有凝聚力的组。换句话说,这些类共享相同的版本号和发布文档,因此组件中的这些项目集合对于作者和用户都有意义。
衡量组件元素之间的内聚程度似乎有点困难,除非在开发或部署过程中破坏 REP 导致痛苦。简而言之,再利用的颗粒就是释放的颗粒。
这一原则强调组件的单一职责,这意味着每个组件不应该有多个变更原因,并且一个基本组件应该只有一个变更轴。
大多数时候,可维护性比可重用性更重要,因为可维护性的衡量标准是每次部署和发布所需的工作量,因此值得牺牲一些可重用性以将相关类收集到单个组件中。该原则试图在组件中保留一系列相关的类,以增加组件的内聚系数。
最后,罗伯特·C·马丁说:“将那些同时因相同原因发生变化的事物聚集在一起。将那些在不同时间或因不同原因发生变化的事物分开。”
该原则指出,“不要依赖于不需要的东西,也不要强迫组件的用户依赖于他们不需要的东西。”
由于类很少单独重用,并且不与属于可重用抽象的其他类协作,因此这些高度依赖的类属于同一组件。此外,CRP 告诉我们哪些类不要放在组件中,以避免组件之间不必要的依赖关系,从而避免在部署没有任何合理关系的组件丛林中浪费大量精力。
作为底线,CRP 告诉我们更多的是哪些类不应该在一起,而不是哪些类应该在一起。该原则表示,彼此不紧密绑定的类不应位于同一组件中。
从软件开发的早期开始,耦合和内聚就一直是双胞胎。这两个评估器原则有助于确定软件元素之间的关系是否安全。组件之间的关系有三个耦合指数。这些原则是:
该原则表示组件的依赖图不应有循环。有一些策略可以打破组件图中的循环路径,包括依赖倒置原则以及创建新包并将公共依赖项移至那里。下图描述了循环依赖性以及通过创建新组件来消除循环依赖性的解决方案。
消除循环依赖
在深入研究这个原则之前,我们应该清楚“稳定”的含义。稳定的事物是需要比平常大量的工作才能改变或移动的事物。元素越稳定,改变所需的功和能量就越多。因此,可以说,当一个组件稳定时,对其进行更改需要大量的工作,包括更改程序的其他部分。例如,看看这个图。
稳定依赖原则
如果“组件D”的公共服务或接口发生变化怎么办?有多少其他组件需要因此而改变?在最坏的情况下,可能还需要更改所有其他组件,这意味着“组件 D”的更改需要与其他组件进行大量压缩工作,因为它有许多依赖组件。根据稳定性的定义,“组件 D”是该图中最稳定的组件。
SDP 表示,如果一个组件是一个经常变化的地方,并且变化频繁,那么它就不是一个稳定的组件,因为该组件的每次变化都会导致其他组件的级联变化。这些类型的组件不能安全地依赖并位于组件依赖关系图的中心。组件的稳定性测量可以使用以下公式进行量化:
在此公式中,数字“Fout”显示组件的输出边缘。该边可以是类和接口之间的任何连接,包括关联、泛化、实现和依赖。“Fin”显示组件的输入连接。因此,如果 I = 0 表示最大稳定分量,则 I = 1 表示最大不稳定分量。下表显示了本文前面的组件图的不稳定性和稳定性因素。
软件的所有部分不一定都是平等的。有些软件包含我们不希望经常更改的最重要的关键业务或技术架构。另一方面,还有其他零部件可能会发生变化。在这些情况下,希望以负担得起的方式进行改变。
也就是说,第一类组件是软件的心脏,其稳定性对我们的产品具有竞争优势。更改第二部分的敏捷性有助于我们以较低的成本协调业务和技术问题。在这种情况下,组件越稳定,它应该越抽象。这意味着软件核心的技术和技术没有具体的决定。