背景介绍
这是我的《架构整洁之道》系列的第十三篇,从这篇文章开始,我们将学习组件之间关系的三条原则:
- 无依赖环原则
- 稳定依赖原则
- 稳定抽象原则
在这些原则中,我们和上一章节《组件聚合》中一样会面临着研发能力和逻辑设计之间的冲突。毕竟,影响组件结构的不仅有技术水平和公司内部政治斗争这两个因素,其结构本身更是不断变化的。
《架构整洁之道》系列:
无依赖环原则
组件依赖关系图中不应该出现环。
一个悲伤的故事
“一觉醒来综合症”不知道大家有没有遇到过,就是前一天你花了很久终于写完了一段代码,并且运行正常,然而第二天上班发现它不能正常工作了,这通常是因为有人在你走后修改了你所依赖的某个组件。
这种情况主要原因是多个程序员同时修改了同一个源代码文件,这在规模较小的项目中不严重,但是随着项目规模增长,这个问题会越来越严重。
为了解决这个问题,演化出来了两个方案:
- 每周构建
- 无依赖环原则
每周构建
每周构建的具体做法是:
在每周的前四天中,让所有的程序员在自己的私有库上工作,忽略其他人的修改,也不考虑互相之间的集成问题。然后在每周五要求所有人将自己所做的变更提交,进行统一构建。
这个方案虽然可以让程序员们有四天可以好好干活,但是到了周五所有人都必须要花费大量的精力来处理前四天留下来的问题。随着项目越来越大,每周五的集成工作会越来越难以按时完成,之后进一步压缩前四天的时间。
之后就出现了一个矛盾点:为了高效率的开发,就不能频繁的进行构建操作,减少构建次数,就会增加项目风险。
消除循环依赖
对于上述情景,我们的解决办法是将研发项目划分为一些可单独发布的组件,这些组件可以交由单人或者某一组程序员来独立完成。
当有人或团队完成某个组件的某个版本时,他们就会通过发布机制通知其他程序员,并给该组件打一个版本号,放入一个共享目录。这样一来,每个人都可以依赖于这些组件公开发布的版本来进行开发,而组件开发者则可以继续去修改自己的私有版本。
这样就不会出现团队之间相互依赖的情况了。任何一个组件上的变更都不会立刻影响到其他团队。每个团队都可以自主决定是否立即集成自己所依赖、组件的新版本。更重要的是,这种方法使我们的集成工作能以一种小型渐进的方式来进行。程序员们再也不需要集中在一起,统一集成相互的变更了 。
但是,如果想要成功推广这个开发流程,就必须控制好组件之间的依赖结构,绝对不能允许该结构中存在着循环依赖关系。
看上面这个图,图中的每个节点都是一个组件,依赖关系就是有向图中的边。在这个图中,不管我们从该图中的哪个节点开始,都不能沿着这些代表了依赖关系的边最终走回到起始点。即这种结构中不存在环,这种结构为有向无环图。
当这个结构是有向无环图时,我们要为其中某个组件发布新版本,就容易判断出哪些组件会受这个变更的影响一一只需要按其依赖关系反向追溯即可:
- 比如修改 Presenters 会直接影响 View 和 Main
当我们需要发布整个系统时,可以让整个过程从下至上来进行。具体来说就是,首先对 Entities 组件进行编译 、测试、发布。随后是 Database 和 Interactors 这两个组件。再紧随其后的是 Presenters 、View、 Controllers ,以及 Authorizer 四个组件。最后是 Main组件。这样一来,整个流程会非常清晰,也很容易。
循环依赖在组件依赖图中的影响
我们对之前的图加上一个依赖关系 Entities-> Authorizer,这样就形成了一个环形依赖。这种依赖就会给我们的项目造成麻烦:
比如:当 Database 组件的程序员需要发布新版本时,他们需要与 Entities 组件进行集成。但现在由于出现了 循环依赖,Database 组件就必须也要与 Authorizer 组件兼容,而 Authorizer 组件又依赖于 Interactors 组件。这样一来,Database 组件的发布就会变得非常困难。在这里,Entities、 Authorizer 及 Interactors 这三个组件事实上被合并成了一个更大的组件。这些组件的程序员现在会互相形成干扰,因为他们在 开发中都必须使用完全相同的组件版本。
这也会影响到项目的:
- 测试粒度
- 单元测试
- 发布流程
- 构建复杂度
打破循环依赖
打破循环依赖有两种方案:
第一种方案为应用依赖反转原则(DIP): 如下图,我们可以创建一个 User 类需要使用的接口,然后将这个接口放入 Entities 组件,并在 Authorizer 组件中继承它。这样就将 Entities 与 Authorizer 之间的依赖关系反转了,自然也就打破了循环依赖关系 。
第二种方案为创建一个新的组件,并让 Entities 与 Authorize 这两个组件都依赖于它。将现有的这两个组件中互相依赖的类全部放入新组件。
第二种解决方案也意味着在需求变更时,项目的组件结构也要随之变更。确实是这样的,随着应用程序的不断演进,其组件结构也会不停地抖动和扩张。
因此,我们必须要持续地监控项目中的循环依赖关系。当循环依赖出现时,必须以某种方式消除它们。为此,我们有时候不可避免地需要创建新的组件,而使整个组件结构变得更大 。
结束语
根据上面的内容,我们可以得出一个无法逃避的结论: 组件结构图是不可能自上而下被设计出来的。它必须随着软件系统的变化而变化和扩张,而不可能在系统构建的最初就被完美设计出来。
所以,组件依赖关系需要随着项目的逻辑设计一起扩张和演进。
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
少年向来不识天高地厚
放眼处皆自负才高八斗
虽是自命风流
倒也坦诚无忧
我爱这样的少年
谦和而狂妄
骄傲又坦然☀️
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨