依赖倒转原则(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
|
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
|
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
|
public class People implements Communicate{ private Communicate communicate; public People(Communicate communicate) { this.communicate = communicate; } public void sendMessage() { communicate.sendMessage(); } } interface Communicate{ void sendMessage(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class People1 implements Communicate1{ private Communicate communicate;
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