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

首先,来对比并分析一下结构化程序设计和面向对象程序设计的区别。

结构化程序设计总是倾向于创建一些高层模块依赖于低层模块、策略依赖于细节的软件结构,该层次结构描述了高层模块怎样调用低层模块。现在,我们就分析一下这样的程序结构中存在什么样的问题。高层模块将所需服务全权委托给底层模块。如果底层模块出现了问题,那么高层模块就被“小人”“出卖”了。因为,底层模块的变动影响了高层模块,高层模块受到了牵连。

我们更希望的是重用高层次的策略设置模块。如果高层模块依赖于低层模块,那么在不同的上下文中重用高层模块就会变得非常困难。如果高层模块独立于低层模块,那么高层模块就可以非常容易地被重用。著名的Hollywood原则这样写到:“Don’t call us,we’ll call you”。低层模块实现了在高层模块中声明并被高层模块调用的接口。这就是所谓的接口所有权倒置。面向对象程序设计的基本思路就是导致接口的所有权,让高层模块定义接口。使低层模块依赖于高层模块。

在软件中,可以使用这样的一个合适的模型来处理问题。每个较高层次都为他所需要的服务声明一个抽象接口,较低的层次实现了这个抽象的接口,每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层。这样,不仅仅倒置了依赖关系,而且倒置了接口的所有权。

面向对象的程序设计倒置了依赖关系结构,使得细节和策略都依赖于抽象,并且常常是客户拥有服务接口。依赖倒置原则是实现许多面向对象技术宣称的好处的基本低层机制。对于构建在变化面前富有弹性的代码页是非常重要的。由于抽象和细节被彼此隔离,所以代码页非常容易维护。

下面就给出什么是【依赖倒置原则】。依赖倒置原则:

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

b) 抽象不应该依赖于细节。细节应该依赖于抽象。

有这样一个简单的启发规则:

l 任何变量都不应该持有一个指向具体类的指针或者引用

l 任何类都不应该从具体类派生

l 任何方法都不应该覆写它的任何基类中的已经实现了的方法

但是,通常会存在一些合理的违反这些规则的情况。比如对于具体但却稳定的类来说,直接引用就非常合理了。通常情况下,客户类声明它们需要的服务接口,仅当客户类需要时才会对接口进行改变。也就是说,有客户类来决定接口的变更。

接下来介绍一下什么是高层策略:它是应用背后的抽象,是那些不随具体细节的改变而改变的真理。

高层模块包含了一个应用程序中的主要策略选择和业务模型。整个程序中,业务模型和策略选择是核心,所以高层模块是程序的本质和灵魂所在。高层模块不能受到底层实现的影响。正确的方式应该是高层模块去影响低层的细节实现模块。包含高级业务规则的模块应该优先并独立于包含实现细节的模块

最后,还是给出一个简单的例子来说明一下本文主要的意图。

现实中有这样的情景:使用开关来打开或关闭电灯。在这里就定义两个类:Button和Light。在Light类中,有这样的两个方法:turnOn()和turnOff()方法——表示电灯的打开或关闭。Button对象可以接受到用户打开或关闭的动作,然后调用Light的turnOn()或turnOff()方法。代码如下:

这里的问题Button类直接依赖于Light类。这样当Light类发生改变时,Button类就会受到影响。同样需求变化时,如果需要使用这个Button类控制门的打开和关闭,那这个Button类的代码几乎是不可重用的。

现在就改变这个程序结构,使Button类不依赖于具体的light实现。想想一下,如果想重用Button类,那么Button类需要什么样的服务呢?Button需要控制一个灯的打开或关闭,或者说门的打开或关闭。以后可能还会去控制空调等。这里面的本质或者说是高层策略就是Button类去控制一些东西的开与关。现在,从中提取出一个抽象接口Switchable,它表示一个事物有开和关的能力。使用Button类控制这个抽象接口,如果需要被Button类操作的事物都可以实现这个接口。代码如下:

使用上面的结构,很大程度上提高了代码的灵活性和可复用性。对于新增的任何需要被Button类控制的类,只需要继承Switchable接口即可,而无须对Button类做出任何改动。

最后总结一下结构化与面向对象之间的区别:无论使用什么语言来编写程序,如果程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是过程化的设计。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结