一、无依赖原则
组件依赖关系图不应该出现环。
我们一定有过这样的经历:
当你花了一整天的时间,好不容易搞定了一段代码,第二天上班时却发现这段代码莫名其妙地又不能工作。这通常是因为有人在你走后修改了你所依赖的某个组件。这种情况叫做”一觉醒来综合症”。
这种综合症的主要病因是:
多个程序员同时修改了同一个源代码文件。虽然在规模相对较小、人员较少的项目中,这种问题或许并不严重,但是随着项目的增长,研发人员的增加,这种每天早上刚上班时都要经历一遍的痛苦就会越来越多。甚至会严重到让有的团队在长达数周的时间内都不能发布一个稳定的项目版本,因为每个人都在不停地修改自己的代码,以适应其他人所提交的变更。
针对上述问题逐渐演化出两种解决方案。一种是”每周构建”,另一种是”无依赖环原则”。
1.每周构建
每周构建方案是中小型项目中很常见的一种管理手段。其具体做法如下:在每周的前四天中,让所有的程序员在自己的私有库上工作,忽略其他人的修改,也不考虑互相之间的集成问题;然后在每周五要求所有人将自己所做的变更提交,进行统一构建。
上述方案确实可以让程序员们每周都有四天的时间放手干活。然而一到星期五,所有人都必须要花费大量的精力来处理前四天留下来的问题。
随着项目越来越大,每周五的集成工作会越来越难以按时完成。而随着集成任务越来越重,周六的加班也会变得越来越频繁。经历过几次这样的加班之后,就会有人提出应该将集成任务提前到星期四开始,就这样一步一步地集成工作慢慢地就要占用掉差不多半周的时间。
事实上,这个问题最终还会造成更大的麻烦。因为如果我们想要保持高效率的开发,就不能频繁地进行构建操作,但是如果我们减少了构建的次数,延长了项目被构建的时间间隔,又会影响到该项目的质量,增大它的风险。整个项目会变得越来越难以构建与测试,团队反馈周期会越来越长,研发质量自然也会越来越差。
2.消除循环依赖
对于上述情景,我们的解决办法是将研发项目划分为一些可单独发布的组件,这些组件可以交由单人或者某一组程序员来独立完成。当有人或团队完成某个组件的某个版本时,他们就会通过发布机制通知其他程序员,并给该组件打一个版本号,放入一个共享目录。这样一来,每个人都可以依赖于这些组件公开发布的版本来进行开发,而组件开发者则可以继续去修改自己的私有版本。
每当一个组件发布新版本时,其他依赖这个组件的团队都可以自主决定是否立即采用新版本。若不采用,该团队可以选择继续使用旧版组件,直到他们准备采用新版本为止。
这样就不会出现团队之间相互依赖的情况了。任何一个组件上的变更都不会立刻影响到其他团队。每个团队都可以自主决定是否立即集成自己所依赖组件的新版本。更重要的是,这种方法使我们的集成工作能以一种小型渐进的方式来进行。程序员们再也不需要集中在一起,统一集成相互的变更了。
如你所述,上述整个过程既简单又很符合逻辑,因而得到各个研发团队的广泛采用。但是,如果想要成功推广这个开发流程,就必须控制好组件之间的依赖结构,绝对不能允许结构中存在着循环依赖关系。如果某项目结构中存在着循环依赖关系,那么”一觉醒来综合症”不可避免。
二、自上而下的设计
根据上述讨论,我们可以得出一个无法逃避的结论:组件架构图是不可能自上而下被设计出来的。它必须随着软件系统的变化而变化和扩张,而不可能在系统构建的最初就被完美设计出来。
组件结构图中的一个重要目标是指导如何隔离频繁的变更。我们不希望哪些频繁变更的组件影响到其他本来应该很稳定的组件,例如,我们通常不会希望无关紧要的GUI变更影响到业务逻辑组件;我们也不希望对报表的增删操作影响到其高阶策略。出于这样的考虑,软件架构师们才有必要设计并且铸造出一套组件依赖关系图来,以便将稳定的高价值组件与常变得组件隔离开,从而起到保护作用。
另外,随着应用程序的增长,创建可重用组件的需要也会逐渐重要起来。这时共同复用原则又会开始影响组件的组成。最后当循环依赖出现时,随着无循环依赖原则的应用,组件依赖关系会产生相应的抖动和扩张。
如果我们在设计具体类之前就来设计组件依赖关系,那么几乎是必然要失败的。因为在当下,我们对项目中的共同闭包一无所知,也不可能知道哪些组件可以复用,这样几乎一定会创造出循环依赖的组件。因此,组件依赖关系是必须要随着项目逻辑设计一起扩张和演进的。