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. 在python中如何设置当前工作目录

    import osos.chdir('要设置的当前目录') >>> import os >>> os.getcwd() 'D:\\Python27' >> ...

  2. 无法启动ArcSDE服务

    ArcSDE服务启动错误:Error (-327), No ArcSDE server license found解决方法:>sdesetup -o update_key -d oracle10 ...

  3. 嵌入式 hi3518平台uboot引导nfs文件系统

    首先贴出来我的bootargs的设置(注没有换行符!!!): setenv bootargs noinitrd mem=64M root=/dev/nfs init=/linuxrc rw nfsro ...

  4. ArcGIS 开发的一些知识学习点

    由于文章太多,不便转载,现主要列举如下: ArcGIS Runtime支持的GP工具列表 ArcGIS Runtime支持的GP工具列表 目录(?)[-] Standard版本Standard 空间分 ...

  5. jquery图片裁切+PHP文件上传

    下载地址:download.csdn.net/source/2745353

  6. CentOS7 安装 swoole

    sudo pecl install swoole 即可安装.安装完后修改php.ini,加入extension=swoole.so 重启 sudo systemctl restart php-fpm ...

  7. 数往知来C#之 String 集合 深拷与浅拷 序列化<五>

    C# 基础常用string方法篇 复习. 1.引用类型与值类型     -->文件的复制与快捷方式的复制 2.垃圾回收 3.静态与非静态   -->如何定义静态成员与静态类   --> ...

  8. vim 7.4 编译安装

    (1): 在安装新版本的Vim之前,你需要卸载原来安装的老版本Vim,依次在终端下执行下列命令: sudo apt-get remove vim sudo apt-get remove vim-run ...

  9. 客户端无法tcp连接上本地虚拟机的问题(最后是linux防火墙问题)

    刚装好裸的centos6.5,很多东西跟以前比都是没有的,所以做起来会遇到很多问题. 今天刚把svn 无法ci的问题解决了,起完服后,发现客户端连不上. 1)端口转发,查看了一下虚拟机的端口转发,发现 ...

  10. Oracle创建删除用户,角色,表空间,导入导出数据库命令总结(转载)

    无意间看到一篇文章,觉得对于ORACLE的新手很实用,特转载,原文出处这里 说明:在创建数据库时输入的密码,是修改系统默认的密码,以system和sysman等系统默认身份登录时要输入的密码就是修改后 ...