我的《冒号课堂》学习笔记设计原则依赖原则

Posted 庸人、自扰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的《冒号课堂》学习笔记设计原则依赖原则相关的知识,希望对你有一定的参考价值。

依赖原则

 

间接原则有一个直接的推论,就是依赖反转原则(Dependency Inversion Principle),简称DIP。依赖反转原则就是高层模块不应依赖低层模块,它们都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。

1)DIP的产生背景

从代码实现的角度来看,高层模块依赖低层模块是很正常的。而且高层模块与低层模块之间往往是决策(policy)与机制(mechanism)之间的关系,高层决策以低层机制为基础,逻辑上似乎也很合理。然而从软件设计上的角度看,这种依赖性却是非常不合理的。依赖通常是具有传递性的(transitive)。这意味着任何依赖高层模块的客户代码最终将依赖低层模块,一旦低层改变就会波及高层的客户代码。退一步讲,即使低层模块不变,高层模块往往也会在不同环境下选取不同的低层模块。如果把这种因地制宜的策略硬编码(hardcode)到高层模块中,它的可维护性和可拓展性将受到严重限制。相对地,高层模块的可重用性和可移植性也会大大降低。为了解决高层模块对低层模块的依赖,人们提出了依赖反转原则DIP。

2)通过抽象模块的引入,高层模块与低层模块实现了解耦,从而消除了前者对后者的依赖。那为什么称‘依赖反转’而不是‘依赖消除’呢?

常言藕断丝连,依赖是无法真正消除的。值是在不同的模块或对象之间进行了转移。DIP在解除了高层模块与低层模块的依赖关系的同时,又在它们与某个抽象模块之间建立了新的依赖关系。虽然依赖没有消除,但是重构后的依赖体系比旧的更加优秀。因为抽象模块脱离了具体的细节,更稳定持久,从而整体的系统结构更具有弹性、更经得起需求和环境的变化。

3)关于依赖反转

DIP倡导依赖抽象,而抽象在此处最大的目的是稳定,因此依赖反转原则可重述为稳定依赖原则(Stable Dependencies Principle,简称SDP):模块应朝稳定的方向依赖。至于为什么说是依赖反转,以数据库应用为例。由于数据库系统开发商各自为政,提供的API不仅复杂晦涩,而且彼此之间差异很大。如果应用程序直接通过数据库系统的API访问数据库,代码将既繁杂又难维护,因而有必要利用间接原则,在应用程序和数据库API之间建立一层中介模块。为保证该模块的通用性和可重用性,还须按照DIP的要求,即新增的高层模块不能依赖低层模块,二者应共同依赖一套稳定的标准规范。下图可以看到Java中的JDBC API是怎么做的。

为了说明问题,先把JDBC API细分为两层,上层是具体类,下层是接口。这些接口在不同数据库系统中有不同的实现,mysql实现只是其中之一。从图中可见高层模块的JDBC API不仅不依赖低层模块,反而是低层的各种实现模块依赖高层模块,因为那些接口是隶属高层模块的。从这个意义上说,依赖关系正好颠倒过来,可不就是依赖反转么?

 

4)具体类应当用于合成而非继承。可是,继承关系是一种强依赖关系,合成不也是一种依赖,同样不满足抽象依赖的原则?

合成比继承的一个优势是它是隐性的、动态的,这使得在合成类与被合成类之间有了插入抽象层的可能。当抽象层利用了多态机制时,这种合成也被称为多态合成(polymorphic composition)。结合此前关于继承用法的忠告,我们便有了合成重用原则(Composite Reuse Principle,简称CRP):多态合成优于(实现)继承(favor polymorphic composite over inheritance)。

 

5)关于DI与DIP的区别

依赖注入DI(Dependency Injection)与依赖反转理出同源,但细究起来还是有细微差异的。首先,它们的侧重点不同。DI强调依赖的来源——完全由外部提供,此依赖自然最好是抽象的,但并非首要要求;DIP则强调依赖的抽象性。其次,它们的应用范围不同。DI是更加具体的策略,一般用于类级别的模块,故为一种设计模式;DIP还可用于类库、组件、架构层等大级别的模块,故为一种设计原则。正如依赖不会消失只会转移,采用DI模式后也一样,通常会安排专门的模块——提供者(provider)或装配器(assembler)——来管理外来依赖的对象,这是一种良好的职能分化,符合SoC原理(关注点分离)。

6)它们和IoC控制反转又有什么区别?

首先约定一个术语——组件(component),具有特定功能规范的可重用的软件单元,是不依赖于应用程序的模块。它的涵盖面很广,可以大到子系统,网络服务(web service)、框架、类库,也可以小到若干个相互协作的类乃至单个类和函数。组件是一个抽象层,也可看作一个广义的抽象数据类型(ADT),因为它们都是通过规范接口为客户提供一系列相关的服务。再说回控制反转IoC,它是一个较为普遍的原则,广泛地应用于框架(framework)设计中。它打破了常规的流程控制模式,把控制权从用户的应用代码转给了底层的某个组件。控制反转并无严格的定义,但可抽象为一种管理组件依赖的机制,以保证组件总能在合适的时候获得合适的依赖。显然,依赖注入是控制反转的一种实现方式,广泛地为轻量级的Spring容器所采用。依赖注入包含两个方面的控制反转;一是组件把创建并管理所须依赖的控制权交给了其他模块;二是组件在服务过程中反向调用来自应用层的(被注入的)依赖。DI容器一般主要强调前者,而本文主要强调后者。此外,依赖查找(Dependency Lookup)是另一种实现方式,广泛地为重量级EJB2.0容器所采用。依赖查找是主动获取依赖(依赖注入是被动获取依赖),Service Locator模式是依赖查找的一种实现。总的来说,依赖反转为实现控制反转提供了一个准则——依赖抽象,而依赖注入则是遵守该准则的一种具体技巧。

 

总结:

 

以上是关于我的《冒号课堂》学习笔记设计原则依赖原则的主要内容,如果未能解决你的问题,请参考以下文章

我的《冒号课堂》学习笔记设计原则内聚原则

设计模式学习笔记 单一职责原则的判定与设计

C++设计模式学习笔记:面向对象设计原则

C++设计模式学习笔记:面向对象设计原则

设计模式学习笔记(十三)“接口隔离原则”

设计模式学习笔记(十三)“接口隔离原则”