java设计模式—依赖倒置原则(DIP)

1、定义

In object-oriented programming, the dependency inversion principle refers to a specific form of decoupling software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details. The principle states:
        A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
        B. Abstractions should not depend on details. Details should depend on abstractions.
The principle inverts the way some people may think about object-oriented design, dictating that both high- and low-level objects must depend on the same abstraction.

----WIKIPEDIA


释义(读者可以试着自己翻译下,个人感觉第二句不好翻,不过蛮有意思的):

在面向对象的程序设计中,依赖倒置原则是指解耦软件模块的特殊的形式。传统的依赖关系建立在高层次,而具体的策略设置应用在低层次上。使用依赖倒置原则,使得高层独立于底层的实现细节,依赖关系被倒置,使得低层次模块依赖于高层次模块的抽象。

原则规定:

A. 高层模块不应该依赖于低层模块,双方都要依赖于抽象类。

B. 抽象类不应该依赖于实现细节,实现细节应该依赖于抽象。

这项原则颠覆了一些人对面向对象程序设计的认识,比如:高层和低层都应该依赖于相同的抽象。


注:在设计中,以抽象类或者接口为基础设计出来的架构要比以具体的实现细节为基础设计出来的架构要稳定。


指导意义:

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 !


实现代码如下:

[java]  view plain  copy
  1. public class AK47Gun {  
  2.     /** 枪执行军人的动作进行射击 */  
  3.     public void shoot() {  
  4.         System.out.println("Fire!!");  
  5.     }  
  6. }  

[java]  view plain  copy
  1. public class Soldier {  
  2.     /** 向着敌人开火 */  
  3.     public void fireEnermy(AK47Gun ak){  
  4.         ak.shoot();  
  5.     }  
  6. }  

这里你会发现一个问题:

如果这个士兵一直使用AK47的话不需要更改什么("需求"不变),但是当某一天他觉得这个不好用,想换一个玩法,你会发现,这个设计太糟糕了,需要去手动的改变Soldier这个类中的具体方法,在士兵与这个AK枪之间是强耦合关系,这样降低了系统的可维护性。


那么接下来怎么设计??


按照依赖倒置的原则,需要实现细节依赖于抽象类,高层模块不要依赖于低层模块。

这里需要抽象出抽象类/接口,来避免细节之间的耦合。让其子类均依赖于其父类高层模块。


这里士兵和枪抽象为接口,让其细节都实现这两个接口,接口与接口之间进行耦合,这样才能保证设计的稳定性。后面在提出需求的时候:我想换一个狙击步枪玩玩。那么我们只需要重新定义个狙击步枪的类实现Gun这个接口并实现其方法,这样在使用的时候直接将狙击步枪的实例交给WYSoldier就可以了。这个需求变更的过程中,我们改变了哪些?

1. 创建了一个SniperRifle类。

2. 在使用的地方创建一个SniperRifle的实例,交给实际的士兵对象去调用。

从这几步你会发现,我们没有改变,WYSoldier这个实现“细节”类,也没有改变AK47Gun这个实现细节类。从测试的角度去看,也就是说,不再需要考虑之前士兵使用AK47Gun还可不可用的问题,因为在之前这个是经过了你的严格测试的。


抽象出来的Gun接口(高层):

[java]  view plain  copy
  1. public interface Gun {  
  2.     /** 枪执行军人的动作进行射击*/  
  3.     public void shoot();  
  4. }  
Gun接口的实现细节(低层):
[java]  view plain  copy
  1. public class AK47Gun implements Gun{  
  2.       
  3.     public void shoot() {  
  4.         System.out.println("AK Fire!!");  
  5.     }  
  6. }  
[java]  view plain  copy
  1. public class SniperRifle implements Gun{  
  2.   
  3.     @Override  
  4.     public void shoot() {  
  5.         System.out.println("SniperRefle Fire!!");  
  6.     }  
  7. }  

士兵接口(高层抽象出来的接口):

[java]  view plain  copy
  1. public interface Soldier {  
  2.     /** 向着敌人开火 */  
  3.     public void fireEnermy(Gun gun);  
  4. }  

士兵的实现细节:

[java]  view plain  copy
  1. public class WYSoldier implements Soldier{  
  2.   
  3.     @Override  
  4.     public void fireEnermy(Gun gun) {  
  5.         gun.shoot();  
  6.     }  
  7. }  

依赖倒置原则核心思想就是要我们面向接口编程。

在实际的项目中,尽量做到的是:

1. 低层模块尽量去依赖抽象类和接口

2. 变量声明类型尽量使用抽象类或者接口类。

3. 继承中遵循里氏替换原则。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值