依赖倒置(Dependence Inversion Principle,DIP)
High level modules should not deppend oupon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
定义中有三层含义:
- 高层模块不应该依于低层模块,两者都应该依赖于其抽象。
- 抽象不应该依赖细节。
-
细节应该依赖抽象。
理解: 高层模块相对低层模块而言。低层模块是不可细分的原子逻辑模块。其组装即是高层模块。
面向对象的语言中,抽象就是接口或者抽象类,不能被实例化。细节就是继承抽象类或实现接口而产生的类,可以被实例化。
java中依赖倒置原则的体现:
1. 模块之间的依赖通过抽象发生,实现类直接不直接发生依赖关系,依赖是通过接口和抽象类产生的。
2. 接口和抽象类不依赖于实现类
3. 实现类依赖于借口或者抽象类
即--面向接口编程 。
案例分析:程序员一般都是多技能型的,在工作时会使用多种编程语言,不同语言的代码会以不同的语法运行。因此有这么一个业务场景,程序员使用不同的语言进行编程,代码以不同的语法运行。
这里有三类对象,编程语言-程序员-业务场景
代码:
Coder.java
public class Coder {
public void code(Java java){
java.run();
}
}
Java.java
public class Java {
public void run(){
System.out.println("代码以Java的语法运行~");
}
}
Client.java
public class Client {
public static void main(String[] args) {
Coder coderA = new Coder();
Java java = new Java();
coderA.code(java);
}
}
这里存在一个问题,如果我们的系统扩展的比较大:存在多种程序员,如后端工程师,前端工程师,每种程序员的技术栈不一样,每种语言的语法也都不一样,那我们的程序员Coder类的code方法需要根据语言的不同重载很多个版本才能满足程序员使用多种语言进行编程的需求。这样一来,每次增加一种程序员,我们需要根据其技能增加code的重载方法;每次增加一种语言,我们需要相应地扩充使用它的程序员的相关方法。ade,杀一个产品经理祭天,这么频繁变态的需求。
贯彻依赖倒置的原则,解决方案应该是这样的:
ICoder.java 程序员接口
public interface ICoder {
public void code(ILang iLang);
}
ILang.java 语言接口
public interface ILang {
void runWithSyntax();
}
CoderA.java A类型程序员
public class CoderA implements ICoder {
@Override
public void code(ILang iLang) {
iLang.runWithSyntax();
}
}
Java.java java语言
public class Java implements ILang {
@Override
public void runWithSyntax() {
System.out.println("程序以Java的语法运行");
}
}
Lisp.java lisp语言
public class Lisp implements ILang {
@Override
public void runWithSyntax() {
System.out.println("程序以Lisp的语法运行");
}
}
Client.java 业务场景类
public class Client {
public static void main(String[] args) {
ICoder coder = new CoderA();
ILang lang = new Java();
coder.code(lang);
}
}
这样的解决方案的好处是:当新增低层模块时,不需要修改与其平行的模块,只需要修改业务场景类,即高层模块即可,更利于拓展系统。
上面的代码里面新增Lisp类以后,直接在Client类里面使用ILang lang = new Lisp();即可,程序员就能使用Lisp的语法进行编程了。避免去修改程序员类的相关方法。程序应该是面向拓展的而不是面向修改的。
依赖传递的三种方法
- 通过构造方法传递
在需要注入接口的类中声明接口,然后通过构造方法的参数初始化。在调用的时候直接传入一个接口的实现类即可。 - Setter方法传递
和构造方法注入相似。 - 接口注入
形同这样的方式是接口注入: ILang lang = new Java();
晚安各位!!!