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. 抽象类不应该依赖于实现细节,实现细节应该依赖于抽象。
这项原则颠覆了一些人对面向对象程序设计的认识,比如:高层和低层都应该依赖于相同的抽象。
注:在设计中,以抽象类或者接口为基础设计出来的架构要比以具体的实现细节为基础设计出来的架构要稳定。
先看下面这样的一个例子:
场景介绍:
玩过CS的都知道,开始的时候需要自己选择用什么枪,那么这里先举这样的一个例子,一个士兵用AK47这个枪,遇到敌人的时候开枪射击,然后Game over !
实现代码如下:
- public class AK47Gun {
- /** 枪执行军人的动作进行射击 */
- public void shoot() {
- System.out.println("Fire!!");
- }
- }
- public class Soldier {
- /** 向着敌人开火 */
- public void fireEnermy(AK47Gun ak){
- ak.shoot();
- }
- }
这里你会发现一个问题:
如果这个士兵一直使用AK47的话不需要更改什么("需求"不变),但是当某一天他觉得这个不好用,想换一个玩法,你会发现,这个设计太糟糕了,需要去手动的改变Soldier这个类中的具体方法,在士兵与这个AK枪之间是强耦合关系,这样降低了系统的可维护性。
那么接下来怎么设计??
按照依赖倒置的原则,需要实现细节依赖于抽象类,高层模块不要依赖于低层模块。
这里需要抽象出抽象类/接口,来避免细节之间的耦合。让其子类均依赖于其父类高层模块。
这里士兵和枪抽象为接口,让其细节都实现这两个接口,接口与接口之间进行耦合,这样才能保证设计的稳定性。后面在提出需求的时候:我想换一个狙击步枪玩玩。那么我们只需要重新定义个狙击步枪的类实现Gun这个接口并实现其方法,这样在使用的时候直接将狙击步枪的实例交给WYSoldier就可以了。这个需求变更的过程中,我们改变了哪些?
1. 创建了一个SniperRifle类。
2. 在使用的地方创建一个SniperRifle的实例,交给实际的士兵对象去调用。
从这几步你会发现,我们没有改变,WYSoldier这个实现“细节”类,也没有改变AK47Gun这个实现细节类。从测试的角度去看,也就是说,不再需要考虑之前士兵使用AK47Gun还可不可用的问题,因为在之前这个是经过了你的严格测试的。
抽象出来的Gun接口(高层):
- public interface Gun {
- /** 枪执行军人的动作进行射击*/
- public void shoot();
- }
- public class AK47Gun implements Gun{
- public void shoot() {
- System.out.println("AK Fire!!");
- }
- }
- public class SniperRifle implements Gun{
- @Override
- public void shoot() {
- System.out.println("SniperRefle Fire!!");
- }
- }
士兵接口(高层抽象出来的接口):
- public interface Soldier {
- /** 向着敌人开火 */
- public void fireEnermy(Gun gun);
- }
士兵的实现细节:
- public class WYSoldier implements Soldier{
- @Override
- public void fireEnermy(Gun gun) {
- gun.shoot();
- }
- }
依赖倒置原则核心思想就是要我们面向接口编程。
在实际的项目中,尽量做到的是:
1. 低层模块尽量去依赖抽象类和接口
2. 变量声明类型尽量使用抽象类或者接口类。
3. 继承中遵循里氏替换原则。
源码已经长传至GitHub:DesignPatterns