1.定义

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

2.定义解读

依赖倒置原则在程序编码中经常运用,其核心思想就是面向接口编程,高层模块不应该依赖低层模块(原子操作的模块),两者都应该依赖于抽象。我们平时常说的“针对接口编程,不要针对实现编程”就是依赖倒转原则的最好体现:接口(也可以是抽象类)就是一种抽象,只要不修改接口声明,大家可以放心大胆调用,至于接口的内部实现则无需关心,可以随便重构。这里,接口就是抽象,而接口的实现就是细节。

如果不管高层模块还是底层模块,它们都依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。

依赖倒转原则其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计(说这句话可能不怎么好理解,再加上一句话就好理解了:面向对象的设计,出发点就是应对变化的问题)。

再举一个生活中的例子,电脑中内存或者显卡插槽,其实是一种接口,而这就是抽象;只要符合这个接口的要求,无论是用金士顿的内存,还是其它的内存,无论是4G的,还是8G的,都可以很方便、轻松的插到电脑上使用。而这些内存条就是具体实现,就是细节。

【错误做法】:抽象A依赖于实现细节b

【正确做法】:抽象A依赖于抽象B,实现细节b实现抽象B

3.优点

  • 代码结构清晰,维护容易。

4.问题提出

类A直接依赖类B,假如需要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

5.解决方案

将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在C#/Java中,抽象指的是接口或者抽象类;在Objective-C中,抽象指的是委托/协议,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。

6.示例

继续发工资的场景:这里,类SalaryManage(类似上面说的类A)负责工资的管理;Director(类似上面说的类B)是总监类,现在我们要通过SalaryManage类来给总监发放工资了,主要代码片段如下所示:

class Director
{
var name: String; init(name: String)
{
self.name = name;
} func calculateSalary()
{
print("\(name)总监的工资是10000");
}
} class SalaryManage
{
func calculateSalary(director: Director)
{
director.calculateSalary();
}
} //调用
let salaryManage = SalaryManage();
salaryManage.calculateSalary(Director(name: "张三")); //打印:张三总监的工资是10000

这样给总监发放工资的功能已经很好的实现了,现在假设需要给经理发工资,我们发现工资管理类SalaryManage没法直接完成这个功能,需要我们添加新的方法,才能完成。再假设我们还需要给普通员工、财务总监、研发总监等更多的岗位发送工资,那么我们就只能不断的去修改SalaryManage类来满足业务的需求。产生这种现象的原因就是SalaryManage与Director之间的耦合性太高了,必须降低它们之间的耦合度才行。因此我们引入一个委托EmployeeDelegate,它提供一个发放工资的方法定义,如下所示:

protocol EmployeeDelegate
{
func calculateSalary();
} class Director: EmployeeDelegate
{
var name: String; init(name: String)
{
self.name = name;
} func calculateSalary()
{
print("\(name)总监的工资是10000");
}
} class SalaryManage
{
func calculateSalary(employee: EmployeeDelegate)
{
employee.calculateSalary();
}
} //调用
let salaryManage = SalaryManage();
salaryManage.calculateSalary(Director(name: "张三")); //打印:张三总监的工资是10000

这样修改后,无论以后怎样扩展其他的岗位,都不需要再修改SalaryManage类了。代表高层模块的SalaryManage类将负责完成主要的业务逻辑(发工资),如果需要对SalaryManage类进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

同样,采用依赖倒置原则给多人并行开发带来了极大的便利,比如在上面的例子中,刚开始SalaryManage类与Director类直接耦合时,SalaryManage类必须等Director类编码完成后才可以进行编码和测试,因为SalaryManage类依赖于Director类。按照依赖倒置原则修改后,则可以同时开工,互不影响,因为SalaryManage与Director类一点关系也没有,只依赖于协议(Java和C#中称为接口)EmployeeDelegate。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。

3.依赖倒置原则(Dependence Inversion Principle)的更多相关文章

  1. 设计模式六大原则(三):依赖倒置原则(Dependence Inversion Principle)

    依赖倒置原则(DIP)定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来: 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码 ...

  2. 依赖倒置原则(Dependence Inversion Principle,DIP)

    依赖倒转原则就是 A.要依赖于抽象,不要依赖于实现.(Abstractions should not depend upon details. Details should depend upon a ...

  3. 依赖倒置(Dependence Inversion Principle)DIP

    关于抽象类和接口的区别,可以参考之前的文章~http://www.cnblogs.com/leestar54/p/4593173.html using System; using System.Col ...

  4. 依赖倒置原则(Dependency Inversion Principle)

    很多软件工程师都多少在处理 "Bad Design"时有一些痛苦的经历.如果发现这些 "Bad Design" 的始作俑者就是我们自己时,那感觉就更糟糕了.那么 ...

  5. DesignPattern系列__03依赖倒置原则

    依赖倒置原则(Dependence Inversion Priiciple,DIP) 介绍 High level modules should not depend upon low level mo ...

  6. 面象对象设计原则之五:依赖倒置原则(The Dependency Inversion Principle,DIP)

    如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现.依赖倒转原则是Robert C. Martin在1996年为“C++Reporte ...

  7. 【面向对象设计原则】之依赖倒置原则(DIP)

    依赖倒转原则(Dependency Inversion  Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象.换言之,要针对抽象(接口)编程,而不是针对实现细节编程. 开闭原则( ...

  8. 依赖倒置原则(Dependence Inversion Principle)

    目录 背景 说明 例子 "倒置"的解释 总结 参考资料 背景 这几天组内的人一起学习DDD,里面再次提到了依赖倒置原则,在这学习过程中,大家又讨论了一下依赖倒置原则. 说明 采用依 ...

  9. 【设计模式六大原则3】依赖倒置原则(Dependence Inversion Principle)

      定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类 ...

随机推荐

  1. 安卓 Input Events(输入事件)

    在安卓中,有不止一种方法从你的应用截取用户交互事件.在你的用户界面中考虑事件,途径就是从用户界面中的一个指定的view对象中捕获事件.该view提供了这样做的方法. 在你用来组成你布局的不同的view ...

  2. 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:4.安装Oracle RAC FAQ-4.4.无法图形化安装Grid Infrastructure

    无法图形化安装: [grid@linuxrac1 grid]$ ./runInstaller Starting Oracle Universal Installer... Checking Temp ...

  3. linux下ubuntu系统安装及开发环境配置

    1.安装系统:别的没什么说的,就是安的时候把网线拔了,不然到 configure apt的时候会卡起很久不走的2.配置网络 编辑/etc/network/interface打开/etc/networt ...

  4. dos攻击

    概念理解 DoS到底是什么?接触PC机较早的同志会直接想到微软磁盘操作系统的DOS--DiskOperationSystem?不,此DoS非彼DOS也,DoS即DenialOfService,拒绝服务 ...

  5. 因為 Hypervisor 未執行,所以無法啟動虛擬機器

    bcdedit /set hypervisorlaunchtype auto https://technet.microsoft.com/zh-tw/magazine/2009.02.hyperv.a ...

  6. Div 自适应屏幕大小

    http://blog.csdn.net/wodetiankong516/article/details/7827256 Background      有时, 我们需要将div或者其他的Elemen ...

  7. linux笔记_20150825_linux有什么好处

    那么多人在用,linux到底有毛好处? 其实我也不太清楚,有人说免费,可是大家用windows也不要钱的.我想在天朝,要钱的软件不多吧.一个子也不用花.真心感谢为人民服务的那些大牛. 现在,除了在ub ...

  8. Configuring and troubleshooting a Schema Provider

    原文:https://codesmith.atlassian.net/wiki/display/Generator/Configuring+and+troubleshooting+a+Schema+P ...

  9. 新手须知设计的法则 Mark

    经常看到一些讲如何学习设计的文章,坦白讲感觉有些千篇一律.且不痛不痒,都说要看点书.学点画.练软件.多观察……唉,练软件这事还要说么,难道你还需要告诉一个人学开发是需要学习编程语言的? 学习是基于过往 ...

  10. 如何在Fedora 22上面配置Apache的Docker容器

    在这篇文章中,我们将会学习关于Docker的一些知识,如何使用Docker部署Apache httpd服务,并且共享到Docker Hub上面去.首先,我们学习怎样拉取和使用Docker Hub里面的 ...