一、组件
组件是部署的单元,组件是可被作为系统一部分部署的最小项。例如:java中的jar等。
组件可以连接,成为单个可执行文件,或者他们聚合起来,成为一个包。
二、组件聚合
- REP: 复用/发布等效原则
- CCP: 共同封闭原则
- CRP: 共同重用原则
1. 复用/发布等效原则(REP)
细粒度的重用是细粒度的发布
复用/发布等效原则(REP)至少字面上看起来很明显,人们想重用软件组件的话,这些组件应当有发布的历史记录,每个记录都有发布号。
- 没法保证所有的可重用组件是相互兼容的
- 发布过程应当生成适当的通知和发布文档
2. 共同封闭原则(CCP)
分离因不同原因改变的方法到不同的类
可维护性比可重用性更加重要,希望应用里的代码在一个组件中变化,不应分散到多个组件中。
这和开闭原则(OCP)相似(组件形式描述):
OCP描述应当对修改时关闭的对扩展时开放的,还强调了集合到同个组件的这些类对同样类型的变化是封闭的。因此,当需求的变化来了,这变化很大程度上把变化的组件的数量降到最低。
和单一职责原则(SRP)的相似性:
SRP告诉我们分离为不同原因改变的方法到不同的类,OCP告诉我们分离为不同原因改变的类到不同的组件。
集合成的这些东西的发生变化应当为同样的原因,并且改变次数相同,分离开这些对于为了不同原因,
改变不同次数的东西。
3.共同重用原则(CRP)
别迫使用户依赖他们不需要的组件
帮助我们决定哪些类应该被放到一个组件中的原则,倾向于描述被重用的类和模块属于那个组件
类很少被孤立地重用,可重用类和其他类相互协作。例如java中的容器类与迭代器,他们彼此耦合,因此应该在一个组件内。
当我们依赖一个组件时,我们想保证我们依赖组件里的每个类,否则,我们得重新部署比我们需要的更多的组件。
和接口隔离原则(ISP)的关联
CRP是ISP的一般化版本,ISP建议我们不要在类中依赖用不到的方法,CRP建议我们在组件中别依赖用不到的类。
别依赖你用不到的东西
组件聚合张力图
REP和CCP是包含型原则–让组件更庞大,CRP是排外的原则–让组件更加小。
三、组件耦合
无环依赖原则
消除依赖循环
为了使之更好运作,你必须管理组件依赖结构,不可以有循环依赖。如果在依赖结构中有循环依赖,那就不能避免“早晨综合症”。
注意一件事:不管从哪个组件开始,不可能沿着依赖关系由绕回起始地组件。这结构就是无环的。叫做有向无环图(DAG)。
寻找受影响的组件:沿着依赖关系相反的指向。例如:Presenters会影响到View、Main.
发布整个系统时,过程是自底向上的,首先对Entities进行编译、测试、发布。
组件依赖图中有循环依赖的影响
Entities,Authorizer,Interactors实际上形成了一个大组件了,在这之上开发的人员都会经历“早晨综合症”。他们将互相钳制因为他们都用到了其他组件相同的版本。
解除循环依赖
有两种主要的机制:
- 引用依赖倒置原则(DIP):通过依赖接口,隔离定义和实现
- 创建一个新的组件:Entities和Authorizer都依赖这个新的组件,把问题都移到新组件中
第二个方法:组件结构在需求变化中是不稳定的–抖动
自上而下设计
模块结构的设计不能自上而下,组件依赖图对应用的功能描述得很少,相反,它们是应用程序的可构建性和可维护性的映射。这就是组件依赖图不能从项目初设计的原因。组件依赖结构的增长和演变是随着系统逻辑设计而来的。
稳定依赖原则
依赖直接的稳定的东西
满足稳定依赖原则(SCP),我们保证你设计成的易于改变的模块不会被更难改变的模块依赖。
稳定性
软件的组件难以改变的原因很多,比如
大小
,复杂性
和清晰度
等特征。我们将忽略这些因素,关注重要的。能确定一点,一个软件组件难以改变
是因为很多其他的组件依赖于它
。具有大量传入依赖关系的组件非常稳定,因为它需要大量工作来协调对所有依赖组件的任何更改。
三个组件依赖于x,所以x有三个好理由不改变。我们说x对这三个组件负责,相反,x不依赖其他的组件,没有其他外来因素改变x,我们说他说独立的–稳定组件。
没有其他组件依赖y,我们称y是无责的,y依赖于其他三个组件,所以可能有来自三个外部的变化源,我们称y是依赖的。
稳定指标
统计组件的依赖
出度
和入度
,这些指标能帮助我们计算组件的位置稳定性
。
- 扇入:被别人依赖的数量
- 扇出:依赖别人的数量
- 不稳定度I :I=扇出/(扇出+扇入),这个值的区间是[0,1],I=0表示组件最为稳定,I=1表示组件最不稳定。
此时I = 1 / (1 + 3) = 1/4
当I值为0时,这意味着该组件依赖其他组件(扇入大于0),该组件(扇出为0)不被其他组件依赖。
java: 通过import语句和全限定类名来计算I
C++: 通过#include来计算
并非所有组件都应该是稳定的
如果一个系统中的所有组件都是最大稳定的,那这个系统将变得不可改变。这并不是理想的状态。我们设计的组件结构应该存在有稳定和不稳定的组件。
设计Flexible组件为可变的,我们得让Flexible具有不稳定性,但实现Stable组件的开发者缺依赖了Flexible组件,这违反了SDP,因为Stable组件的不稳定值I比Flexible组件的要小,这导致Flexible组件不再容易改变,对Flexible的改变将迫使我们处理依赖于它的包括Stable组件等等的组件。
DIP依赖倒置,创建一个名为US的接口放到一个新组件UServer,确保类US中包含了类U要用的所有方法,之后我们让类C实现接口US。迫使两个组件都依赖UServer,UServer是稳定的(I=0),Flexible保持了我们期望的不稳定性(I=1),所有的组件顺着箭头方向都是不稳定度I值递减的。
稳定抽象原则
抽象度的衡量
抽象度A值的区间为[0,1],A=0意味着组件里没有抽象性的类,A=1意味着组件仅仅只包含抽象性的类。
A: 这个值是是一个组件里接口和抽象类总数和这个组件的类总数的比值。
- Nc: 组件里类的总数
- Na: 组件里的接口和抽象类总数
- A: 抽象度 A = Na/Nc