求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
OO设计的 DIP依赖倒置原则
 

作者:Eric_Jo,发布于2011-12-16

 

依赖倒置原则的2个重要方针

A. High level modules should not depend upon low level modules. Both shoulddepend upon abstractions.

高层模块不应该依赖于低层模块,二者都应该依赖于抽象

B. Abstractions should not depend upon details. Details should depend uponabstractions.

抽象不应该依赖于细节,细节应该依赖于抽象

概念解说:

依赖:在程序设计中,如果一个模块a使用/调用了另一个模块b,我们称模块a依赖模块b。

低层模块:往往在一个应用程序中,我们有一些低层次的类,这些类实现了一些

基本的或初级的操作,我们称之为低层模块;

高层模块:另外有一些高层次的类,这些类封装了某些复杂的逻辑,并且依赖于

低层次的类,这些类我们称之为高层模块。

我们现在来看看依赖有几种,依赖也就是耦合,分为下面三种:

----- 零耦合(Nil Coupling)关系,两个类没有依赖关系,那就是零耦合。

----- 具体耦合(Concrete Coupling)关系,两个具体的类之间有依赖关系,

那么就是具体耦合关系,如果一个具体类直接引用另外一个具体类就会发

生这种关系。

-----抽象耦合(Abstract Coupling)关系,这种关系发生在一个具体类和一个抽

象类之间,这样就使必须发生关系的类之间保持最大的灵活性。

为什么叫做依赖倒置(DependencyInversion)呢?

面向对象程序设计相对于面向过程(结构化)程序设计而言,依赖关系被倒置了。因为传统的结构化程序设计中,高层模块总是依赖于低层模块。

依赖倒置(Dependence InversionPrinciple)原则讲的是:要依赖于抽象,不要依赖于具体。

简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

抽象不应当依赖于细节;细节应当依赖于抽象;

要针对接口编程,不针对实现编程。

问题的提出

Robert C. Martin氏在原文中给出了“Bad Design”的定义:

1. It is hard to change because every change affects too many other parts ofthe system.(Rigidity)

系统很难改变,因为每个改变都会影响其他很多部分。

2. When you make a change, unexpected parts of the system break. (Fragility)

当你对某地方做一修改,系统的看似无关的其他部分都不工作了。

3. It is hard to reuse in another application because it cannot be disentangledfrom the current application. (Immobility)

系统很难被另外一个应用重用,因为你很难将要重用的部分从系统中分离开来。

导致“Bad Design”的很大原因是“高层模块”过分依赖“低层模块”。

一个良好的设计应该是系统的每一部分都是可替换的。

如果“高层模块”过分依赖“低层模块”:

一方面一旦“低层模块”需要替换或者修改,“高层模块”将受到影响;

另一方面,高层模块很难可以重用。

比如,一个Copy模块,需要把来自Keyboard的输入复制到Print,

即使对Keyboard和Print的封装已经做得非常好,但如果Copy模块里直接使用Keyboard与Print,

Copy任很难被其他应用环境(比如需要输出到磁盘时)重用。

问题的解决:

为了解决上述问题,Robert C. Martin氏提出了OO设计的Dependency Inversion Principle (DIP) 原则。

DIP给出了一个解决方案:

在高层模块与低层模块之间,引入一个抽象接口层。

High Level Classes(高层模块) -->

Abstraction Layer(抽象接口层) -->

Low Level Classes(低层模块)

抽象接口是对低层模块的抽象,低层模块继承或实现该抽象接口。

这样,高层模块不直接依赖低层模块,高层模块与低层模块都依赖抽象接口层。

当然,抽象也不依赖低层模块的实现细节,低层模块依赖(继承或实现)抽象定义。

Robert C. Martin氏给出的DIP方案的类的结构图:

PolicyLayer -->

MechanismInterface(abstract) -->

MechanismLayer -->

UtilityInterface(abstract) -->

UtilityLayer

类与类之间都通过Abstract Layer来组合关系。

实例说明DIP

反面例子:

缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。

解决办法一:

将Light作成Abstract,然后具体类继承自Light。

优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根 据"开放-封闭"原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。

缺点:如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。

解决方法二:

优点:更为通用、更为稳定。

总结

DIP要求客户端依赖于抽象耦合,抽象不应当依赖于细节,细节应当依赖于抽象(Abstractionsshould not depend upon details. Details should depend upon abstractions),这 个原则的另外一个表述就是"四人团"强调的那个:要针对接口编程,不要对实现编程(Program to aninterface, not an implementation),程序在需要引用一个对象时,应当尽可能的使用抽象类型作为变量的静态类型,这就是针对接口编程的含义。DIP是达到"开-闭"原则的途径。

要做到DIP,用抽象方式耦合是关键。由于一个抽象耦合总要涉及具体类从抽象类继承。并且需要保证在任何引用到某类的地方都可以改换成其子类,因此,LSP是DIP的基础。DIP是OOD的核心原则,设计模式的研究和应用都是用它作为指导原则的。DIP虽然强大,但是也很难实现。另外DIP是假定 所有的具体类都会变化,这也不是全对,有些具体类就相当稳定。使用这个类的客户端就完全可以依赖这个具体类而不用再弄一个抽象类。


 
相关文章

UML概览
UML图解:用例图(Use case diagram )
UML图解:活动图(activity diagram )
UML图解:类图(class diagram )
UML图解:对象图(object diagram)
UML图解:顺序图( sequence diagram )
 
相关文档

模型跟踪:跟踪图、矩阵、关系(建模工具EA)
自定义表格(Custom Table)在EA中的使用
元素的详情浏览控制
UAF 1.2规范解读(DMM 和 UAFML )
EA中支持的各种图表
EA中的界面原型建模
 
相关课程

UML与面向对象分析设计
UML + 嵌入式系统分析设计
业务建模与业务分析
基于SysML和EA进行系统设计与建模
基于模型的需求管理
业务建模 & 领域驱动设计


如何向妻子解释OOD
OOAD与UML笔记
UML类图与类的关系详解
UML统一建模语言初学
总结一下领域模型的验证
基于 UML 的业务建模


面向对象的分析设计
基于UML的面向对象分析设计
UML + 嵌入式系统分析设计
关系数据库面向OOAD设计
业务建模与业务架构
使用用例进行需求管理


某航空IT部门 业务分析与业务建模
联想 业务需求分析与建模
北京航管科技 EA工具与架构设计
使用EA和UML进行嵌入式系统分析
全球最大的茶业集团 UML系统分析
华为 基于EA的嵌入式系统建模
水资源服务商 基于EA进行UML建模
更多...