依赖倒转原则
2015-07-11 11:30
134 查看
【概念】
抽象不应该依赖于细节,细节应该依赖于抽象。A. 高层模块不应该依赖低层模块。两个都应该依赖抽象。
B. 抽象不应该依赖于细节,细节应该依赖于抽象。
【解释】
(百度百科解释)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。面向过程的程序设计过程中,上层调用下层,上层依赖于下层,当下层需求发生更改的时候上层的代码就必须要做相应的变动。面向对象的程序设计则考虑:在一般性的情况下,抽象变化的可能性比较小,让客户端依赖于抽象,然而实现的细节也依赖于抽象,只要抽象不变动,那么客户端的程序也就不需要做任何的变动。也就是要针对抽象编程,而不是针对细节编程。
【实例】(百度百科)
某公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。根据所给的需求,模拟这一过程。分析该系统发现可以设计两个基础类“福特车”和“本田车”。然后设计一个自动驾驶类来调用这两个基础类。UML类结构图可以绘制如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace denpendrevese { public class HondaCar { 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; } public void RunCar() { if(type == CarType.Ford) { fcar.Run(); } else { hcar.Run(); } } public void TurnCar() { if(type == CarType.Ford) { fcar.Turn(); } else { hcar.Turn(); } } public void StopCar() { if(type == CarType.Ford) { fcar.Stop(); } else { hcar.Stop(); } } } class Program { static void Main(string[] args) { AutoSystem dr = new AutoSystem(AutoSystem.CarType.Honda); dr.RunCar(); } } }
假设现在需求发生更改,公司的业务发展变大,加入新的汽车品牌“丰田Toyota”。这时候就需要添加“丰田车”相对应的类了。但是问题也就来了:在加入类之后,还要在AutoSystem类中添加相应的代码。加入一个类倒也不费事,但是如果是加入多个类的话,程序的结构就变得十分的复杂了。显然这是和依赖倒转原则相违背的。在上述代码的编写过程中,我们一直是在针对细节(各种的汽车类)进行编程,而不是正对抽象进行编程的。上层模块(AutoSystem)在下层模块(添加Toyota汽车类)发生变动的时候,就要做相应的修改。这对代码的维护产生很大的负面影响。
现在按照依赖倒转原则重新分析上述问题,如果去掉上层代码和下层代码之间的依赖,也就是为其设计相应的接口,画出UML图如下:
那么,现在添加Toyota类的时候就不用再去修改上层的AutoSystem类,因为AutoSystem类不依赖于具体的汽车类(细节),AutoSystem类依赖的是ICar这一接口(抽象)。
按照上述分析编写代码,得到:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace denpendrevese { public interface ICar { void Run(); void Turn(); void Stop(); } public class HondaCar : 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 ToyotaCar : 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; } public void RunCar() { icar.Run(); } public void TurnCar() { icar.Turn(); } public void StopCar() { icar.Stop(); } } class Program { static void Main(string[] args) { AutoSystem dr = new AutoSystem(new ToyotaCar()); dr.RunCar(); dr.TurnCar(); dr.StopCar(); Console.ReadKey(); } } }
上述代码中,在添加具体的汽车类后,仅需要添加添加汽车类,并且在客户端代码对其进行实例化就可以了,并不用去修改其他的内容。究其根本就在于,在两个具体的事物中间,我们并不是去关心他们的联系,而是去设计一个他们之间相互调用的接口,就像电脑主板上的内存接口一样。如果我们是电脑组装人员,我们不去管内存条到底是怎么设计实现的(不关注细节),我们只关心他们的接口是不是兼容,如果兼容,那就直接使用就好了。
【总结】
一言以蔽之:要针对抽象编程,而不是针对细节编程。高层的模块通过接口来调用低层的模块,而不是直接对其进行调用。依赖倒转原则的UML图可表示如下:相关文章推荐
- 2-3树—检索数据类型
- 微信支付
- C语言及程序设计[套餐]课程主页
- BJOI2015 Day2
- 静态库与动态库详细剖析
- 奇妙的算法—把有界凸空间中所有点输出的算法讨论
- UI3_UILabel
- TFS(Team Foundation Server)敏捷使用教程(四):工作项跟踪(1)
- [BZOJ 4103] [Thu Summer Camp 2015] 异或运算 【可持久化Trie】
- SWIFT中正则表达式验证邮箱
- jquery Validate
- java synchronized详解
- [LeetCode][Java] Substring with Concatenation of All Words
- iOS常见警告解决
- SQL集合运算
- 精选9个值得学习的 HTML5 效果【附源码】
- 《Memcache技术学习》系列技术文章整理收藏
- adb基本命令
- Silkroad 与 Tesseract 通信协议 QuestionModel
- 自己新建一个支持c++11的Qt工程模板