知识卡片
循环依赖的连锁代价与两种打破方式
内容
[[ADP无依赖环原则从一觉醒来综合征到组件化发布]]要求的DAG结构一旦被打破,代价会迅速扩散:假设Entities组件的User类反过来依赖了Authorizer组件的Permissions类,就形成了循环依赖。这时Database组件要发布新版本、需要和Entities集成,却因为循环依赖被迫也要兼容Authorizer,而Authorizer又依赖Interactors——Entities、Authorizer、Interactors这三个原本独立的组件事实上被合并成了一个更大的组件,各自的程序员必须使用完全相同的版本、互相干扰;连测试Entities这样看似局部的工作,也要被迫把Authorizer和Interactors一起拉进来集成测试。更棘手的是,一旦依赖图里存在环,就几乎不可能按正确顺序构建组件,这在Java这类需要从编译好的二进制文件里读取声明信息的语言中会造成尤其棘手的问题,且随着组件数量增多,构建问题会呈几何级数增长。打破循环依赖有两种主要机制:一是应用依赖反转原则(DIP),在Entities组件里创建一个User需要的接口,让Authorizer组件去实现它,从而把Entities对Authorizer的依赖方向彻底反转;二是新建一个组件,把Entities与Authorizer之间原本互相依赖的那些类都挪进这个新组件,让两者转而共同依赖它。第二种方案意味着组件结构会随需求变化不断”抖动”和扩张——这不是设计缺陷,而是必须持续监控循环依赖、并在它出现时果断消除的常态化工作。
参考来源
- 位置:《架构整洁之道》第14章《组件耦合》"循环依赖在组件依赖图中的影响""打破循环依赖""'抖动'"(源文件:_epub-src/text/part0013_split_003.html)
- 结论依据:原文用Entities/Authorizer/Interactors循环依赖的例子说明多个组件被迫合并成一个更大组件、测试与发布连带受累的具体后果,并给出应用DIP反转依赖方向、新建共同依赖的组件两种打破循环的机制,说明组件结构因此需要持续抖动扩张,直接支撑本卡片结论。
- 原始内容:Entities、Authorizer及Interactors这三个组件事实上被合并成了一个更大的组件……应用依赖反转原则(DIP)……创建一个新的组件,并让Entities与Authorize这两个组件都依赖于它……随着应用程序的不断演进,其组件结构也会不停地抖动和扩张。