1、定义
释义(读者可以试着自己翻译下,个人感觉第二句不好翻,不过蛮有意思的):
这项原则颠覆了一些人对面向对象程序设计的认识,比如:高层和低层都应该依赖于相同的抽象。
注:在设计中,以抽象类或者接口为基础设计出来的架构要比以具体的实现细节为基础设计出来的架构要稳定。
指导意义:
1)高层模块不应该依赖低层模块
2)抽象不应该依赖细节
3)细节依赖抽象
在Java中的表现为:面向接口编程 OOP
1)模块间的依赖通过抽象发生,实现类间不发生直接的依赖关系,其依赖关系通过接口或者抽象类产生;
2)接口或抽象类不依赖于实现类
3)实现类依赖于接口或者抽象类
依赖倒置原则可以减少类之间的耦合性,提高系统的稳定性;降低并行开发引起的风险。
要并行开发就要解决模块间的依赖关系,依赖倒置原则正好解决这个问题。
在Java中,只要定义变量就必然要有类型,一个变量可以有两种类型,表面类型和实际类型,UserDao是表面类型,UserDaoImpl是实际类型。
依赖的三种写法:依赖是可以传递的,只要做到抽象依赖,即使是多层的依赖也没关系。
1)构造函数传递依赖对象
2)Setter方法传递依赖对象
3)接口方法中传入依赖对象
最佳实践:
1)每个类尽量都有接口或者抽象类
2)变量的表面类型尽量是接口或者抽象类
3)不从具体类派生类,也就是派生类尽量继承抽象类或者实现接口。
4)尽量不覆写基类的方法,只实现;
倒置的概念就是所谓的抽象依赖。
示例:
参考:http://blog.youkuaiyun.com/wangyang1354/article/details/51167071
场景介绍:
玩过CS的都知道,开始的时候需要自己选择用什么枪,那么这里先举这样的一个例子,一个士兵用AK47这个枪,遇到敌人的时候开枪射击,然后Game over !
实现代码如下:
这里你会发现一个问题:
如果这个士兵一直使用AK47的话不需要更改什么("需求"不变),但是当某一天他觉得这个不好用,想换一个玩法,你会发现,这个设计太糟糕了,需要去手动的改变Soldier这个类中的具体方法,在士兵与这个AK枪之间是强耦合关系,这样降低了系统的可维护性。
那么接下来怎么设计??
按照依赖倒置的原则,需要实现细节依赖于抽象类,高层模块不要依赖于低层模块。
这里需要抽象出抽象类/接口,来避免细节之间的耦合。让其子类均依赖于其父类高层模块。
这里士兵和枪抽象为接口,让其细节都实现这两个接口,接口与接口之间进行耦合,这样才能保证设计的稳定性。后面在提出需求的时候:我想换一个狙击步枪玩玩。那么我们只需要重新定义个狙击步枪的类实现Gun这个接口并实现其方法,这样在使用的时候直接将狙击步枪的实例交给WYSoldier就可以了。这个需求变更的过程中,我们改变了哪些?
1. 创建了一个SniperRifle类。
2. 在使用的地方创建一个SniperRifle的实例,交给实际的士兵对象去调用。
从这几步你会发现,我们没有改变,WYSoldier这个实现“细节”类,也没有改变AK47Gun这个实现细节类。从测试的角度去看,也就是说,不再需要考虑之前士兵使用AK47Gun还可不可用的问题,因为在之前这个是经过了你的严格测试的。
抽象出来的Gun接口(高层):
Gun接口的实现细节(低层):
士兵接口(高层抽象出来的接口):
士兵的实现细节:
依赖倒置原则核心思想就是要我们面向接口编程。
在实际的项目中,尽量做到的是:
1. 低层模块尽量去依赖抽象类和接口
2. 变量声明类型尽量使用抽象类或者接口类。
3. 继承中遵循里氏替换原则。