设计模式六大原则之 依赖倒置原则

依赖倒置原则

依赖倒置原则(Dependence Inversion Principle,DIP) 它的原始定义是这样的:

定义

High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

翻译过来,包括三层含义

1.高层模块不应该依赖低层模块,两者都应该依赖其抽象;

2.抽象不应该依赖细节;

3.细节应该依赖抽象。

在Java语言中的表现就是:

1.模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;

接口或抽象类不依赖于实现类;

实现类依赖接口或抽象类。

更加精简的定义就是:

面向接口编程 – OOD(Object-Oriented Design, 面向对象设计)的精髓之一.

举例

中国象棋

每个棋子都拥有“移动(move)”的动作,但每个棋子的这个方法又有不同,比如“马走日,象走田”就是体现了移动的不同。

1
2
3
public interface IChess{
public void move();
}
1
2
3
public interface IPlayer{
public void handle(IChess chess);
}
1
2
3
4
5
public class Ma implements IChess{
public void move(){
System.out.println("马走日");
}
}
1
2
3
4
5
public class Xiang implements IChess{
public void move(){
System.out.println("象走田");
}
}
1
2
3
4
5
6
public class RedPlayer implements IPlayer{
public void handle(IChess chess){
System.out.println("红方移动");
chess.move();
}
}
1
2
3
4
5
6
public class BlackPlayer implements IPlayer{
public void handle(IChess chess){
System.out.println("黑方移动");
chess.move();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Clint{
public static void main(String args[]){
IChess ma = new Ma();
IChess xiang = new Xiang();

IPlayer redPlayer = new RedPlayer();
IPlayer blackPlayer = new BlackPlayer();

redPlayer.handle(ma);
blackPlayer.handle(xiang);
}
}
1
2
3
4
5
6
运行结果:

红方移动
马走日
黑方移动
象走田

究竟何为依赖倒置

首先看依赖二字,依赖体现共通的逻辑,对接口方法的实现就是体现了子类依赖其接口或抽象类。

那么何为“倒置”呢?“倒置”是相对“正置”而言的,人类正常的思维方式都是正置的,怎么理解呢?就是我们接触到的都是具体实现类,比如马走日,就是指我们接触到的是马所行走的方式,而倒置就是指我们根据系统设计的角度,来找出事物间的抽象模型,抽取出抽象间的依赖,来代替传统思维中对事物间依赖关系的描述。比如“司机开车”就是抽象的依赖关系,其具体的实现包括“卡车司机开卡车”,“公交车司机开小轿车”等。

依赖倒置原则的三个实现:

1.构造函数传递依赖对象

1
2
3
4
public interface IDriver {
//司机的驾驶方法
public void drive();
}
1
2
3
4
5
6
7
8
9
10
11
public class Driver implements IDriver{
private ICar car
//构造函数注入
public Driver(ICar _car){
this.car = _car;
}
//司机的驾驶方法
public void drive(){
this.car.run();
}
}

2.Setter方法传递依赖对象

1
2
3
4
5
6
public interface IDriver{
//车辆型号
public void setCar(ICar car);
//司机的驾驶方法
public void drive();
}
1
2
3
4
5
6
7
8
9
10
public class Driver implements IDriver{
private ICar car;
public void setCar(ICar car){
this.car = car;
}
//司机的驾驶方法
public void drive(){
this.car.run();
}
}

3.接口声明依赖对象

上面对依赖倒置的举例就是接口声明的方式,该方法也叫做接口注入

建议遵循的规则

1.每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备

2.变量的表面类型尽量是接口或者是抽象类

3.任何类都不应该从具体类派生

4.尽量不要复写基类的方法

5.结合里氏替换原则使用