设计模式七大原则之依赖倒转原则 (Dependence Inversion Principle)

依赖倒转原则(DIP: Dependence Inversion Principle)也说依赖倒置原则,包含三层含义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。依赖倒转(倒置)的中心思想就是面向接口编程。

举个我们日常通信的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author sunys
*/
public class Dependenceinversion {
public static void main(String[] args) {
People people = new People();
people.sendMessageBySMS(new SMScommunicate());
}

private static class SMScommunicate{
public void sendMessage(){
System.out.println("使用手机短信发送信息!");
}
}

private static class People{
public void sendMessageBySMS(SMScommunicate smScommunicate){
smScommunicate.sendMessage();
}
}
}

在很久之前,互联不发达时,我们通信一般采用手机发送短信或者打电话,如上面的代码,一切正常。但是,随机互联网的普及更多的通信方式出现了,例如微信、QQ等,需求场景发生变化,那么最简单的做法就是新增微信和QQ的通讯类,并且给People增加相应的方法。显然,这种方法不能满足多变性需求,且不稳定。所以,我们使用另一种思路解决,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* @author sunys
*/
public class Dependenceinversion1 {
public static void main(String[] args) {
People people = new People();
people.sendMessage(new SMScommunicate());
people.sendMessage(new QQcommunicate());
people.sendMessage(new Wechatcommunicate());
}

private interface Communicate{
void sendMessage();
}

private static class SMScommunicate implements Communicate{
public void sendMessage(){
System.out.println("使用手机短信发送信息!");
}
}

private static class QQcommunicate implements Communicate{

public void sendMessage() {
System.out.println("使用QQ发送信息!");
}
}
private static class Wechatcommunicate implements Communicate{

public void sendMessage() {
System.out.println("使用微信发送信息!");
}
}

private static class People{
public void sendMessage(Communicate communicate){
communicate.sendMessage();
}
}
}

根据依赖倒置原则,在新的解决方案中,我们引入了一个通讯工具的接口Communicate,让几种不同的通信方式去实现接口,这样修改后,无论以后怎样扩展,都不需要再修改People类了。

依赖关系传递的三种方式

  • 接口声明依赖

在上面日常通信的例子中,我们在用的就是接口声明依赖的方式,该方法也叫做接口注入。

  • 构造函数传递依赖对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 构造函数传递依赖对象
* @author sunys
*/
public class People implements Communicate{
private Communicate communicate;
//构造函数注入
public People(Communicate communicate) {
this.communicate = communicate;
}
public void sendMessage() {
communicate.sendMessage();
}
}
interface Communicate{
void sendMessage();
}
  • Setter方法传递依赖对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Setter方法传递依赖对象
* @author sunys
*/
public class People1 implements Communicate1{
private Communicate communicate;
//Setter依赖注入

public void setCommunicate(Communicate communicate) {
this.communicate = communicate;
}

public void sendMessage() {
communicate.sendMessage();
}
}
interface Communicate1{
void sendMessage();
}

在java中,抽象指的是接口或抽象类,细节就是具体的实现类,相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。在实际的开发中,低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好;变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化;使用继承时遵循 里氏代换原则(Liskov Substitution Principle)

附:本次演示的项目地址
https://github.com/syshlang/java-design-principle