编程绕不开的一个话题:耦合
耦合其实是个中性词,代码模块之间要相互协作完成任务,就必定要耦合。耦合即:一方对另一方的假设。你不可能对协作的对象不做任何假设,所以其实有些耦合是必须的。我们要解决的是非必要的,不良的耦合。
如无必要,不要使代码耦合,过度耦合会加速代码腐烂(比如:难加新功能,难修 bug)
对另一方做尽可能少的假设其实只是手段之一,而且这种手段势必会让代码变得更加抽象复杂。另一种手段就是:制定标准和建立知识库,大家用共同的标准和共有的知识去沟通,就会非常顺畅,反之则寸步难行。所以当你觉得你们的项目程序写得稀烂,先不要关注过度耦合的问题,先看看是不是连标准都没制定,公共知识库都没有建立,人员不知道不遵守既定标准和知识。
怎么写出低耦合的代码
- 模块的单向依赖关系
- 接口的正交性和紧凑性
- 提供原子化的最底层 API + 胶水层 API(根据业务,对原子 API 做一些比较常用的组合)
- 既要防御式编程,也要提供模块和接口的最佳实践
单向依赖
模块之间尽可能的单向依赖,做成像树状结构,而不要像网状结构。
正交性
正交性是指一个模块提供的 API 中,多个方法之间是否有重复的功能。
如果有重复功能,正交性就差。
想象一下,调节电视的声音的时候,亮度也会跟着一起变化。
通常,正交性高的模块更稳定,不会因为上层业务变化而被迫修改代码。
好的 API 内部的多个方法之间不应该有任何重复功能,只实现正交的机制。
如果感觉拆得太细使用不便,应该在底层 API 之外包装出一层 Helper、Utility 组成的胶水层。
胶水层调用底层原语 API 来实现常用模式供上层使用。
对于胶水层中的模块,对正交性的要求可以稍低一些。
注意上层代码既可以直接调用正交的底层 API,又可以调用胶水层的常用模式。
紧凑性
紧凑性是指一个模块提供的 API 中,公有方法总数必须很少,每个方法的参数也必须很少。
《Unix 编程艺术》上说一个模块不要超过 7 个方法,不然就很难理解。
总之,单向依赖、正交性、紧凑性这三个指标都很务实,有客观方法可以度量。
不同级别之间的耦合
软件是由不同级别的概念层组成,不同级别的概念层具有不同的职责,不同级别的概念层中存在不同的耦合:有方法级别、类级别、包级别、协议级别、语言级别、数据流级别、数据库级别、业务级别……
总结
耦合的本质是假设,假设越多,被打破的机率就越高,所以软件的可靠性就越低。低耦合代表的是在软件不同级别的概念上只依赖它需要依赖的,从而达到它本身的修改不至于造成其它系统的非必要影响,反之亦然。
在软件开发过程中,我们应该管理这些耦合,不论在哪个级别上。应该经常考虑如果这些假设被打破了,会给系统带来哪些风险。