复用与依赖原则

复用与依赖原则

  • REP(复用、发布等同原则):软件复用的最小粒度应等同于其发布的最小粒度。直白地说,就是要复用一段代码就把它抽成组件。该原则指导我们组件拆分的粒度。

  • CCP(共同闭包原则):为了相同目的而同时修改的类,应该放在同一个组件中。CCP 原则是 SRP 原则在组件层面的描述。该原则指导我们组件拆分的粒度。对大部分应用程序而言,可维护性的重要性远远大于可复用性,由同一个原因引起的代码修改,最好在同一个组件中,如果分散在多个组件中,那么开发、提交、部署的成本都会上升。

  • CRP(共同复用原则):不要强迫一个组件依赖它不需要的东西。CRP 原则是 ISP 原则在组件层面的描述。该原则指导我们组件拆分的粒度。

相信你一定有这种经历,集成了组件 A,但组件 A 依赖了组件 B、C。即使组件 B、C 你完全用不到,也不得不集成进来。这是因为你只用到了组件 A 的部分能力,组件 A 中额外的能力带来了额外的依赖。如果遵循共同复用原则,你需要把 A 拆分,只保留你要用的部分。

REP、CCP、CRP 三个原则之间存在彼此竞争的关系,REP 和 CCP 是黏合性原则,它们会让组件变得更大,而 CRP 原则是排除性原则,它会让组件变小。遵守 REP、CCP 而忽略 CRP,就会依赖了太多没有用到的组件和类,而这些组件或类的变动会导致你自己的组件进行太多不必要的发布;遵守 REP、CRP 而忽略 CCP,因为组件拆分的太细了,一个需求变更可能要改 n 个组件,带来的成本也是巨大的。

模块化

这个在架构上已经是通识了,也是应对复杂的直接和有力的手段。模块化如果上升到原则层面可以描述为:高内聚和松耦合。通过什么样的指导原则或者手段来划分模块呢?

  • 将相同变化速率的代码放到一起
  • 将概念上同一个东西的代码放到一起
  • 需要放到一起的东西封装在起来,通过接口进行通信
  • 设计接口要简洁有力,只给需要的东西,不多给

需要注意的就是模块化是有成本的。抽象的成本是会蠕变,其次通信的成本。在应用模块化的时候需要检测这些成本,过犹不及。抽象是会蠕变的,有可能让系统剩下的部分变的不那么清晰。这个可以称之为蠕变(creep)或者说是背负(carry on)成本。

依赖管理

让不稳定的依赖于稳定的,这里在架构上也是通识,但在实现中花样有点多。也不是很容易理解。常见的依赖管理手段

  • 依赖倒置(DIP)。通过接口在编译期将调用者(caller)依赖于被调用者(callee)的依赖倒过来。让 callee 依赖于稳定的接口。这里面有个假设就是接口比实现稳定,很多时候这是成立的。需要留意接口常常变化的情况,这时候依赖倒置得不偿失。

-机制和策略分离。机制是 How to do,策略是是 What to do。机制受制于基础科学的发展,通常很缓慢。所以相对稳定。而策略是多变的业务规则,通常不稳定。通过机制和策略的分离,提高了机制的复用率。

  • 接口和引擎分离。Interface 随着受众而变化,但是引擎在针对覆盖的业务部分却相当稳定。一个很好的例子就是 ffmpeg 和其围绕他的一系列 GUI 工具。

  • 通过自动化,生成器来让稳定的元数据(Metadata)可以按需生成的合适数据形式。这也是 DRY 和 SPOT(Single Point Of Truth)的应用。

上一页
下一页