设计模式之依赖倒转原则
一 原理
依赖倒置原则又称依赖倒置原则:
抽象不应该依赖细节,细节应该依赖于抽象。说白了,就是针对接口编程,不要针对实现编程。
依赖倒置原则包含三层含义:
1)高层模块不应该依赖低层模块,两者都应该依赖其抽象;
2)抽象不应该依赖细节;
3)细节应该依赖抽象。
看了上面的解释相信大家会和我一样会有一些疑问在脑海里(你存在我深深的脑海里)下面来详细说一说吧:
1)为什么要针对接口编程,而不是针对实现编程呢?
很简单的一个例子,我们现在使用的电脑有各式的品牌,联想、神舟、戴尔等等,
电脑需要用到鼠标,键盘;假设鼠标、键盘是针对某一个品牌的机器实现去做的话,那么我们将会遇到什么问题呢?
那么我们市面上的键盘和鼠标就都是各式各样的,有一天鼠标,或键盘坏了,我们要怎么去买呢?难道记住这个电脑是什么品牌,什么型号,还有什么类型的去买么?这样会疯掉的。现在我们的电脑基本上都是使用USB接口的了,无论是键盘也好,鼠标也好,我们只要买USB接口的就可以使用了,同时,使用USB接口还可以有其他的扩展,只要实现了,这个接口,实现怎么样都没关系,例如,实现了USB接口的小台灯,只要接上USB线就可以照明了;又如实现了USB
接口的充电器,接到我们的电脑上就可以充电了。
2)高层模块不应该依赖低层模块,两者都应该依赖其抽象又如何理解呢?这个问题也可以这么问:为什么要叫倒置(倒转)呢?
在面向过程的开发中,为了使用常用的代码可以复用,一般都会把这些常用的代码写成许许多多函数的程序库,这样我们做新项目的时候,就去调用这些函数就可以了。
例如:我们做的项目大多要访问数据库,所以我们就把数据库的代码写成了函数,每次做新项目时就去调用这些函数,这也就是高层依赖于低层模块了。
问题就出现在这里了,我们在做新项目的时候,会发现业务逻辑的高层模块是一样的,我们希望能重用这些高层模块,但是这些高层模块和低层模块的数据库绑定在一起了,这样我们就没办法复用这些高层模块了,这样就
。
如果我们的高层模块和低层模块都依赖于抽象,具体一点就是依赖于接口或抽象类,只要接口够稳定,那么任何一个的更改都不用担心其他受到影响了。
3)为什么依赖了抽象的接口或是抽象类,就不怕更改了呢?要解决这个问题,先看看里氏替换原则。
里氏替换原则:
一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化;
简单的说:子类型必须能够替换掉它们的父类型。例如:企鹅在生物学分类上是属于鸟的,但是在编程中,企鹅就不能以父类的(鸟)的身份出现。
假设有一个鸟的父类,拥有一个会飞的方法fly(),我们是不能让企鹅继承于鸟的,这样当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正的被复用,
而子类也能够在父类的基础上添加新的行为。例如:猫是继承动物类的,以动物的身份拥有吃、喝、跑、叫等行为,
可当某一天,我们需要狗、牛、羊也拥有类似的行为,由于它们都是继承于动物,所以除了更改实例化的地方,程序其他地方就需要改变了。
正是由于子类型的可替换性才使得使用父类类型在的模块在无需修改的情况下就可以扩展。咱们来看看UML图:
高层模块依赖于接口或抽象类,低层模块实现接口或抽象类。依赖倒置原则其实就是谁也不依靠谁,除了约定的接口,这样大家都可以灵活自由了。
二 案例
背景1:公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。于是有人做出了分析如图一。
对于图一分析:我们定义了一个AutoSystem类,一个FordCar类,一个HondaCar类。FordCar类和HondaCar类中各有三个方法:Run(启动Car)、Turn(转弯Car)、Stop(停止Car),当然了一个汽车肯定不止这些功能,这里只要能说明问题即可。AutoSystem类是一个自动驾驶系统,自动操纵这两辆车。
代码实现:
public class HondaCar {
![](http://moto0421.iteye.com/images/icon_star.png)
- <span style="font-size: medium;"> public void Run() {
- Console.WriteLine("本田开始启动了");
- }
- public void Turn() {
- Console.WriteLine("本田开始转弯了");
- }
- public void Stop() {
- Console.WriteLine("本田开始停车了");
- }
- }
- public class FordCar
- {
- public void Run() {
- Console.WriteLine("福特开始启动了");
- }
- public void Turn() {
- Console.WriteLine("福特开始转弯了");
- }
- public void Stop() {
- Console.WriteLine("福特开始停车了");
- }
- }
- public class AutoSystem
- {
- public enum CarType{ Ford, Honda };
- private HondaCar hcar = new HondaCar();
- private FordCar fcar = new FordCar();
- private CarType type;
- public AutoSystem(CarType type) {
- this.type = type;
- }
- private void RunCar() {
- if (type == CarType.Ford) {
- fcar.Run();
- }
- else {
- hcar.Run();
- }
- }
- private void TurnCar() {
- if (type == CarType.Ford) {
- fcar.Turn();
- }
- else {
- hcar.Turn();
- }
- }
- private void StopCar() {
- if (type == CarType.Ford) {
- fcar.Stop();
- }
- else {
- hcar.Stop();
- }
- }
- }</span>
代码分析:上面的程序确实能够实现针对Ford和Honda车的无人驾驶,但是软件是在不断变化的,软件的需求也在不断的变化。
背景2:公司的业务做大了,同时成为了通用、三菱、大众的金牌合作伙伴,于是公司要求该自动驾驶系统也能够安装在这3种公司生产的汽车上。于是我们不得不变动AutoSystem:
public class AutoSystem
![](http://moto0421.iteye.com/images/icon_star.png)
- <span style="font-size: medium;"> {
- public enum CarType { Ford, Honda ,Bmw};
- HondaCar hcar = new HondaCar();
- FordCar fcar = new FordCar();
- BmwCar bcar = new BmwCar();
- private CarType type;
- public AutoSystem(CarType type)
- {
- this.type = type;
- }
- private void RunCar()
- {
- if (type == CarType.Ford)
- {
- fcar.Run();
- }
- else if (type == CarType.Honda)
- {
- hcar.Run();
- }
- else if (type == CarType.Bmw)
- {
- bcar.Run();
- }
- }
- private void TurnCar()
- {
- if (type == CarType.Ford)
- {
- fcar.Turn();
- }
- else if (type == CarType.Honda)
- {
- hcar.Turn();
- }
- else if (type == CarType.Bmw)
- {
- bcar.Turn();
- }
- }
- private void StopCar()
- {
- if (type == CarType.Ford)
- {
- fcar.Stop();
- }
- else if (type == CarType.Honda)
- {
- hcar.Stop();
- }
- else if (type == CarType.Bmw)
- {
- bcar.Stop();
- }
- }</span>
}分析:这会给系统增加新的相互依赖。随着时间的推移,越来越多的车种必须加入到AutoSystem中,这个“AutoSystem”模块将会被if/else语句弄得很乱,而且依赖于很多的低层模块,只要低层模块发生变动,AutoSystem就必须跟着变动,
它最终将变得僵化、脆弱。
导致上面所述问题的一个原因是,含有高层策略的模块,如AutoSystem模块,依赖于它所控制的低层的具体细节的模块(如HondaCar()和FordCar())。如果我们能够找到一种方法使AutoSystem模块独立于它所控制的具体细节,那么我们就可以自由地复用它了。我们就可以用这个模块来生成其它的程序,使得系统能够用在需要的汽车上。OOD给我们提供了一种机制来实现这种“依赖倒置”。
这儿有一个“AutoSystem”类,它包含一个“ICar”接口。这个“AutoSystem”类根本不依赖于“FordCar”和“HondaCar”。所以,依赖关系被“倒置”了:“AutoSystem”模块依赖于抽象,那些具体的汽车操作也依赖于相同的抽象。
于是可以添加ICar
public interface ICar
![](http://moto0421.iteye.com/images/icon_star.png)
- <span style="font-size: medium;"> {
- void Run();
- void Turn();
- void Stop();
- }
- public class BmwCar:ICar
- {
- public void Run()
- {
- Console.WriteLine("宝马开始启动了");
- }
- public void Turn()
- {
- Console.WriteLine("宝马开始转弯了");
- }
- public void Stop()
- {
- Console.WriteLine("宝马开始停车了");
- }
- }
- public class FordCar:ICar
- {
- public void Run()
- {
- Console.WriteLine("福特开始启动了");
- }
- public void Turn()
- {
- Console.WriteLine("福特开始转弯了");
- }
- public void Stop()
- {
- Console.WriteLine("福特开始停车了");
- }
- }
- public class HondaCar:ICar
- {
- public void Run()
- {
- Console.WriteLine("本田开始启动了");
- }
- public void Turn()
- {
- Console.WriteLine("本田开始转弯了");
- }
- public void Stop()
- {
- Console.WriteLine("本田开始停车了");
- }
- }
- public class AutoSystem
- {
- private ICar icar;
- public AutoSystem(ICar icar)
- {
- this.icar = icar;
- }
- private void RunCar()
- {
- icar.Run();
- }
- private void TurnCar()
- {
- icar.Turn();
- }
- private void StopCar()
- {
- icar.Stop();
- }
- }</span>
现在AutoSystem系统依赖于ICar 这个抽象,而与具体的实现细节HondaCar、FordCar、BmwCar无关,所以实现细节的变化不会影响AutoSystem。对于实现细节只要实现ICar 即可,即实现细节依赖于ICar 抽象。
综上:
一个应用中的重要策略决定及业务模型正是在这些高层的模块中。也正是这些模型包含着应用的特性。但是,当这些模块依赖于低层模块时,低层模块的修改将会直接影响到它们,迫使它们也去改变。这种境况是荒谬的。应该是处于高
层的模块去迫使那些低层的模块发生改变。应该是处于高层的模块优先于低层的模块。无论如何高层的模块也不应依赖于低层的模块。而且,我们想能够复用的是高层的模块。通过子程序库的形式,我们已经可以很好地复用低层的模块了。当高层的模块依赖于低层的模块时,这些高层模块就很难在不同的环境中复用。但是,当那些高层模块独立于低层模块时,它们就能很简单地被复用了。这正是位于框架设计的最核心之处的原则。
总结:依赖倒置原则
A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。
B.抽象不应该依赖于具体,具体应该依赖于抽象。
设计模式之依赖倒转原则相关推荐
- 【设计模式】依赖倒转原则
依赖倒转原则 依赖倒转原则是指的特点有∶ 高层模块不应该依赖低层模块,二者都应该依赖其抽象 抽象不应该依赖细节,细节应该依赖抽象 依赖倒转(倒置)的 中心思想 是面向接口编程 依赖倒转原则是基于这样的 ...
- 【设计模式】依赖倒转原则(Dependence Inversion Principle)
高层模块不应该依赖低层模块,两者都应该依赖其抽象:抽象不应该依赖细节,细节应该依赖抽象.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合. 1 案例分析(组装电脑 ...
- java 依赖倒置_JAVA设计模式之依赖倒转原则
3.1 依赖倒置原则的定义 依赖倒置原则(Dependence Inversion Principle,简称DIP)这个名字看着有点别扭,"依赖"还"倒置",这 ...
- 设计模式:依赖倒转原则(记录一)
依赖倒置原则(Dependence Inversion Principle,简称DIP)这个名字看着有点别扭,"依赖"还"倒置",这到底是什么意思?依赖倒置原则 ...
- 【大话设计模式】依赖倒转原则
[依赖倒转原则] 高层模块不应该依赖底层模块,两个都应该依赖抽象. 抽象不依赖于细节,细节依赖依赖于抽象. 解释:针对接口编程,不对实现编程. 高层模块依赖低层模块的含义: 面向过程的开发时,为了使得 ...
- 依赖倒转原则_Java设计模式的七大原则
Java设计模式的七大原则 里氏代换原则 里氏代换原则是对"开-闭"原则的补充.实现"开-闭"原则的关键步骤就是抽象化.而基类与子类的继承关系就是抽象化的具体实 ...
- Java设计模式——依赖倒转原则
一.什么是依赖倒转原则? 依赖倒转原则讲的是,要依赖于抽象,不要依赖于具体. 实现"开-闭"原则的关键是抽象化,并且从抽象化导出具体化实现."开-闭"原则是面向 ...
- Java设计模式之设计的6大原则(开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,最少知道原则,合成复用原则)
1. 开闭原则 核心思想:一个对象对外扩展开发,对修改关闭 意思就是:对类的改动是通过增加代码进行的,而不是修改现有的代码. 也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证 ...
- [设计模式]依赖倒转原则
代码如下: #include <iostream> #include <string>using namespace std;//银行工作人员 class BankWorker ...
最新文章
- 激光-视觉-IMU-GPS融合SLAM算法梳理和代码讲解
- mysql having子句_mysql having子句学习
- Larbin 安装遇到的问题(fedora)
- Linux磁盘分区及文件系统管理之基础概念
- python安装unittest_python 自动化测试 (一):安装 requests,unittest,HTMLTestRunner
- 并发编程中的“冷知识”(更新中)
- 大数据_MapperReduce_Hbase的优化_RowKey设计原则---Hbase工作笔记0028
- 安卓使用MediaPlayer自定义音频视频播放器
- 第十三次CCF CSP认证(2018年3月)真题URL映射
- android shell强制删除文件夹_手机文件夹都是英文,看不懂、又不敢删?教你如何辨别、释放内存...
- es配置中文和拼音分词器
- 组合排列中重复数问题
- 人类的精神寄托和生命的终极关怀——宗教
- gsyVideoPlayer直播短视频回放,集成腾讯播放器
- day03 python基础
- android 坐标度分秒转换工具,百度地图API详解之坐标系转换
- 603. Consecutive Available Seats
- PHP长字符串表示方法
- wordpress友联_一段代码开启WordPress友情链接管理
- Android原生h5互跳控制,Android原生与H5交互方式
热门文章
- 密码学系列之:feistel cipher
- CentOS 编译Hadoop 2.6 32位
- Leet Code OJ 14. Longest Common Prefix [Difficulty: Easy]
- Leet Code OJ 171. Excel Sheet Column Number [Difficulty: Easy]
- MyBatisPlus注入公共Sql问题
- mybatis mapper.xml入参
- Redis学习之Sentinel(四)
- 28行代码AC——Minimum Sum LCM UVA - 10791(最大质因子)
- php 1 打印出来,php 怎么强制打印错误
- pythondistutils安装_安装msi后的python distutils