上一章我们讨论的是如何形成组件。这一章我们关注的是组件之间的关系。
无依赖环原则
组件依赖关系图中不应该出现环。
当我们第二天醒来,发现之前运行好的代码突然间不能工作了。这很有可能是因为别人修改了我们所依赖的组件。主要原因是多个程序员同时修改了同一个源代码文件导致的。在项目小,人数少的项目中,这种情况或许不严重。项目大,人数多时,人们每天就都会很忙碌,因为要修改自己的代码,以此达到适应别人对代码的修改,严重时会导致几周都发布不了一个稳定的版本。
在过去 ,针对这个问题,电信行业演化出了两个针对性方案,第一种是每周构建
,第二种是无依赖环原则(ADP)
。
每周构建
在每周的前四天,让所有程序员在自己的私有库上工作,忽略其他人的修改,也不考虑互相集成问题。周五将所有人的变更提交,进行统一构建。
这对小项目来说可行,可是大型项目或者人数众多,会发现周五的时间远远不够用,集成所花费的时间越来越多。
并且,如果我们想要高效开发,就不能频繁构建。但如果减少构建次数,又会影响项目质量。最终整个项目将越来越难构建与测试,质量也会越来越差。
消除环依赖
我们将项目,划分为一些可单独发布的组件,这些组件可以交由担任或者某组程序员独立完成。当完成一个组建的某个版本时,通过发布机制通知其他程序员,并给该组件打一个版本号,放入一个共享目录中。这样,每个人都可以根据公开组件的版本号来进行自己的组件开发。
每个组件发布新版本时,依赖这个组件的团队都可以自行选择依赖哪个版本。这样就不会出现到处相互依赖的情况了,因为任何一个变更都不会影响到其他团队。最关键的是,程序员也不用集中在一起统一构建了。
但是如果想要推广这个开发流程,就必须控制好组件之间的依赖结构,绝对不能允许该结构中存在循环依赖关系。
下图是一个拥有循环依赖的错误例子:
这种依赖会带来什么问题?
当Database
组件需要发布新版本时,就需要Entities
组件进行继承,但是出现了循环依赖,所有Database
也需要与Authorizer
和Interactors
进行兼容。这种循环让Entities
,Interactors
,Authorizer
形成了一个大组件,并且都需要使用同一个版本。
这个问题在测试过程中会更加明显,因为这三个组件你不能单独拎出来测试了,每次都需要其他两个组件来参与。
并且这种依赖将会很容易出错。
这种问题,在Go
语言中,体现的非常明显,直接就不让你编译通过。
打破循环依赖
我们可以用2
种方法打破组件中的循环依赖,并将依赖图转化为DAG
。
- 依赖反转(DIP):使其依赖与接口,被依赖方去实现这个接口。
- 创建新组件,将依赖关系全部放入新组件,使得原有的依赖关系,转换为同时依赖另一个组件,打破循环。
抖动
采用第二种解决方案就意味着需求的变更,项目结构将会发生变化,因为多加了个Permissions
组件。随着项目的演进,这种为了解决依赖的而产生的新组件,和项目的扩张,就叫做抖动。所以我们必须时刻监控循环依赖关系。
自上而下的设计
我们可以得出一个结论,组件架构图是不可能自上而下被设计出来的,要做好这个心理准备,它不可能在项目之初就被完美设计出来,它是跟随着系统的演进而调整出来的。
因为人们的直觉就是,组件和系统功能是相互对应的。实际上不是这样的。组件依赖结构图并不是用来描述应用程序功能的,比如可以看Database
和Entities
组件,它并没有描述功能。所以组件依赖图不能在项目的开始阶段就被设计出来。
组件依赖图的作用和目标就是指导如何隔离频繁的变更。随着项目演进,根据组件的组合原则和各类设计原则(SOLID)
,组件将不可避免地发生变更,所以我们需要时刻监控组件结构依赖图。
稳定依赖原则
依赖关系必须要指向更稳定的方向。
设计这件事不可能是完全静止的,如果我们要设计一个可维护的软件,那么就必须将变更敏感和不敏感的部分隔离。任何一个预期多变更改的组件A
,都不应该被一个难于修改的组件B
所依赖,否则这个组件A
也会变得难以修改。
稳定性
稳定性应该与变更的频繁度没有直接关系,而应当与变更所需要的耗费的工作量有关。例如一枚竖着的硬币,轻轻一碰就会倒,而一张桌子,想要掀翻它要花费不少力气。
稳定组件
下图中的X
就是稳定性组件,因为它不依赖别的组件。同时三个组件依赖于它,所以X
有三个不应该被修改的原因,因为它要对三个组件负责。
不稳定组件
下图中的Y
就是不稳定的组件。因为Y
依赖于三个组件,没有其他组件依赖Y
。Y
的变更,通常可能是由三个被依赖的组件所带来的,也就是说变更可能比较频繁。Y
是有依赖性的组件。
jiagou
当然不可能要求所有的组件都是稳定组件。但是我们需要将稳定组件和不稳定组件得依赖关系处理好,并做适当的隔离。
稳定抽象原则
高阶策略中应该都是稳定组件,越抽象越高阶,就应当越稳定。
稳定性指标
书里,讲的大部分为如何量化稳定性,是一种测量工具。先不看不记录。