知识卡片
DIP真正该关注的是易变具体实现,及稳定抽象层的四条编码守则
内容
依赖反转原则要求源代码层面多引用抽象类型、少引用具体实现,但把这条原则当成不容分说的金科玉律并不现实——软件系统不可避免要依赖一些具体实现,比如Java的String类根本不可能被强行抽象化,源码里对java.lang.String的依赖既无法避免也不该刻意回避。关键的区分在于:String类本身极其稳定(几乎不会被修改,可修改的内容也受到严格控制),稳定的操作系统或平台设施同理,程序员不必为这类依赖担心意外变更;DIP真正要提防的,是系统内部那些开发中、会经常变动(volatile)的具体实现模块。这引出一个反直觉但重要的判断:接口比实现更稳定——每次修改抽象接口一定要跟着改具体实现,但反过来修改具体实现却很少需要改接口,所以优秀的架构师会花大力气把接口设计得尽量不需要改动,追求”不改接口就能加新功能”。据此可以归纳出四条具体编码守则:多用抽象接口、避免使用多变的具体实现类(对象创建过程通常靠抽象工厂来严格约束);不要在具体实现类上创建衍生类(继承是最强、最难修改的源代码依赖关系,用得要格外小心);不要覆盖包含具体实现的函数(覆盖并不能消除继承而来的依赖关系,唯一的控制手段是创建抽象函数、再为它提供多种实现);避免在代码里写入与具体实现或其他易变事物相关的名字(这基本是DIP的另一种表达)。
参考来源
- 位置:《架构整洁之道》第11章《DIP:依赖反转原则》引言"稳定的抽象层"(源文件:_epub-src/text/part0012_split_005.html)
- 结论依据:原文用java.lang.String类作为无法避免且不必回避的稳定具体实现的例子,说明DIP真正要提防的是易变的具体实现模块,并给出"接口比实现更稳定"的判断依据及四条具体编码守则,直接支撑本卡片结论。
- 原始内容:这个类被修改的情况是非常罕见的……我们主要应该关注的是软件系统内部那些会经常变动的(volatile)具体实现模块……所以我们可以认为接口比实现更稳定……应在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类。