敏捷软件开发 – DIP 依赖倒置原则

DIP 依赖倒置原则

  1. 高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
  2. 抽象不应该依赖于细节。细节应该依赖于抽象。

  依赖于低层模块的高层模块意味着什么?正是高层模块包含了应用程序中重要的策略选择和业务模型。这些高层模块使得其所在的应用程序区别于其他。然而,如果这些高层模块依赖于低层模块,那么对于低层模块的改动会直接影响到高层模块,从而迫使它们依次做出改动。如果高层模块独立于低层模块,那么高层模块就可以非常容易地被重用。该原则是框架设计的核心原则。

层次化

  糟糕的层次关系。

  更为适合的模型。每个较高层都为它所需要的服务声明一个抽象接口。较低的层次实现了这些抽象接口。每个高层类都通过该抽象接口使用下一层。这样高层就不依赖于低层。低层反而依赖与在高层中声明的抽象服务接口。这不仅解除了PolicyLayer对于UtilityLayer的传递依赖关系,甚至也解除了PolicyLayer对于MechanismLayer的依赖关系。

倒置的接口所有权

  倒置不仅仅是依赖关系的倒置,它也是接口所有权的倒置。我们通常会认为工具库应该拥有它们自己的接口。但是当应用了DIP时,我们发现往往是客户拥有抽象接口,而它们的服务者则从这些抽象接口派生。

  这就是著名的Hollywood原则:"Don‘t call us, we‘ll call you.(不要调用我们,我们会调用你。)"低层模块实现了在高层模块中声明并被高层模块调用的接口。

  通过这种倒置的接口所有权,对于MechanismLayer和UtilityLayer的任何改动都不会再影响到PoliyLayer。而且,PolicyLayer可以在定义了符合PolicyServiceInterface的任何上下文中重用。这样,通过倒置这些依赖关系,我们创建了一个更灵活、更持久、更易改变的结构。

  这里所说的所有权仅仅是指接口是随拥有它们的客户程序发布的,而非实现它们的服务器程序。接口和客户程序位于同一个包或者库中。这就迫使服务器程序库或者包依赖于客户程序库或者包。

  当然,有时我们会不想让服务器程序依赖于客户程序,特别是当有多分客户程序但是服务器却仅有一份时。在这种情况下,客户程序必须得遵循服务接口,并把它发布到一个独立的包中。

依赖于抽象

  程序中所有的依赖关系都应该终止于抽象类或者接口。

  •   任何变量都不应该持有一个指向具体类的引用。
  •   任何类都不应该从具体类派生。
  •   任何方法都不应该重写它的任何基类中已经实现了的方法。

  当然,每个程序都会有违反该启发规则的情况。有时必须创建具体类的实例,而创建这些实例的模块将会依赖于它们。此外,该启发规则对于那些虽然是具体但却稳定的来来说似乎不大合理。如果一个具体类不太会改变,并且也不会创建其他类似的派生类,那么依赖于它并不会造成损害。

  比如,在大多数系统中,描述字符串的类都是具体的。例如,在C#中的String。该类是稳定的。也就是说,它不太会改变。因此,直接依赖于它不会造成伤害。

  如果一个不稳定的类的接口必须变化是,这个变化一定会影响到表示该类的抽象接口。这种变化破坏了由抽象接口维系的隔离性。

  由此可知,该启发规则对问题的考虑有点儿简单了。另一方面,如果看得更远一点,认为是客户模块或者层来声明它们需要的服务接口,那么仅当客户需要时才会对接口进行改变。这样,改变实现抽象接口的类就不会影响到客户。

找出潜在的抽象

  接口可以被许多不同的客户使用,并被许多不同的服务者实现。这样,接口就需要独立存在而不属于任何一方。在C#中,可以把它放在一个单独的命名控件和库中。

结论

  使用传统的过程化程序设计所创建出来的依赖关系结构、策略是依赖于细节的。这是糟糕的,因为这样会使策略受到细节的改变的影响。面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户程序拥有服务接口。

  事实上,这种依赖关系倒置正是好的面向对象设计的标志所在。使用何种语言编程是无关紧要的。如程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是过程化的设计。

  依赖倒置原则是实现许多面向对象技术所宣称的好处的基本低层机制。它的正确应用对于创建可重用的框架来说是必需的。同时它对于构建在变化面前富有弹性的代码也是非常重要的。由于抽象和细节彼此隔离,所以代码也非常容易维护。

摘录自:[美]RobertC.Martin、MicahMartin著,邓辉、孙鸣译 敏捷软件开发原则、模式与实践(C#版修订版) [M]、人民邮电出版社,2013、115-121、

时间: 2024-10-13 01:56:33

敏捷软件开发 – DIP 依赖倒置原则的相关文章

[敏捷设计]5.DIP依赖倒置原则

一.定义 1.高层模块不应该依赖低层模块,二者都应该依赖抽象 2.抽象不应该依赖于细节.细节应该依赖于抽象 二.层次化 1.简单介绍 结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的.受控的接口向外提供了一组内聚的服务. 对于这个陈述的简单理解可能会致使设计者设计出类似下图的结构. 图中,高层的Policy层使用了低层的Mechanism层,而Mechanism层又使用了更细节的Utility层.这样,Policy层对下面的Utility层的改动都是敏感的. 这种依赖关系是

六大设计原则(三)DIP依赖倒置原则

原文:六大设计原则(三)DIP依赖倒置原则 依赖倒置原则DIP(Dependence Inversion Principle) 依赖倒置原则的含义 高层模块不能依赖低层模块,二者都应该依赖其抽象. 抽象不应该依赖于细节. 细节应该依赖抽象. 什么是高层模块?低层模块? 每一个原子逻辑就是低层模块,原子逻辑再组就是高层模块. 什么是抽象和细节? 抽象是抽象类,不可被实例化. 细节是实现类,比如实现的接口或继承抽象类的子类,可以被实例化. 表现在Java语言中就是面向接口编程 模块间的依赖是通过抽象

敏捷软件开发 – ISP 接口隔离原则

如果类的接口不是内聚的,就表示该类具有“胖”接口.换句话说,类的“胖”接口可以分解成多组方法.每一组方法服务于一组不同的客户程序. ISP承认有一些对象确实需要有非内聚的接口,但是ISP建议客户程序不应该看到它们作为单一的类存在.相反,客户程序看到的应该是多个具有内聚接口的抽象基类. 接口污染 考虑一个安全系统.在这个系统中,有一些Door对象,可以被加锁和解锁,并且Door对象知道自己是开着还是关着.这个Door编码成一个接口,这样客户程序就可以使用那些符合Door接口的对象,而不需要依赖于D

敏捷软件开发 – OCP 开放-封闭原则

软件实体(类.模块.函数等)应该是可以扩展的,但是不可修改的. 如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味.OCP建议我们应该对系统进行重构,这样以后对系统再进行这样那样的改动时,就不会导致更多的修改.如果正确地应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不必改动已经正常运行的代码. OCP概述 遵循开放-封闭原则设计出的模块具有两个主要的特征.它们是: 对于扩展是开放的(open for extension).这意味着模块的行

敏捷软件开发 – SRP 单一职责原则

SRP:单一职责原则  一个类应该只有一个发生变化的原因. 为何把两个职责分离到单独的类中很重要呢?因为每一个职责都有变化的一个轴线.当需求变化时,该变化会反映为类的职责的变化.如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个. 如果一个类承担的职责过多,就等于把这些职责耦合在了一起.一个职责发生变化可能会削弱或抑制这个类完成其他职责的能力.这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏. 有两个不同的应用程序使用Rectangle类.一个应用程序是有关计算几何

敏捷软件开发的12个原则

作为一个软件工程师,软件设计和开发是最重要的技能,但是,从整个产品的角度上讲,项目管理能力比开发能力更重要,本文摘自Robert大叔的<敏捷软件开发>,粗体是Robert大叔的话,细体是我的理解. 1.持续.尽早交付有价值的软件以满足客户,是我们优先要做的首要任务. 以逐渐增加功能的方式经常性地交付系统和最终质量之间有非常强的相关性.交付得越频繁,最终产品的质量就越高. 自顶向下地设计软件,按照功能优先级逐步开发,定期交付可运行的版本.这个原则看起来简单,但是对软件设计有非常高的要求,因为随着

敏捷软件开发 – LSP Liskov替换原则

Liskov替换原则:子类型(subtype)必须能够替换掉它们的基类型(basetype). 违反LSP的情形 对于LSP的违反常常会导致以明显违反OCP的方式使用运行时类型检查.通常,会使用一个显式的if语句或者if/else链去确定一个对象的类型,以便于可以选择针对该类型的正确行为. struct Point { double x, y;} public enum ShapeType { square, circle }; public class Shape { private Shap

敏捷软件开发要点【转载】

下面的文字来自于<敏捷软件开发 原则.模式和实践>一书,作者是Robert C. Martin.我把这些文字发布在这里,希望对敏捷软件开发还不是很了解的朋友所有帮助.我推崇这本书,是因为它提出了许多有价值的软件项目管理的理念,以及软件设计思想和方法,其中,很多可以直接用在我们的工作中,或用来指导我们的工作----敏捷软件开发是务实的. 一.敏捷软件开发宣言 我们正在通过亲身的实践以及帮助他人实践,揭示更好的软件开发方法.通过这项工作,我们认为: 个体和交互 胜过 过程和工具 可以工作的软件 胜

依赖倒置原则(Dependency Inversion Principle)

很多软件工程师都多少在处理 "Bad Design"时有一些痛苦的经历.如果发现这些 "Bad Design" 的始作俑者就是我们自己时,那感觉就更糟糕了.那么,到底是什么让我做出一个能称为 "Bad Design" 的设计呢? 绝大多数软件工程师不会在设计之初就打算设计一个 "Bad Design".许多软件也在不断地演化中逐渐地降级到了一个点,而从这个点开始,有人开始说这个设计已经腐烂到一定程度了.为什么会发生这些事情呢?