依赖倒转原则 (Dependence Inversion Principle)
定义:程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
-
问题:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险
-
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
-
例子:CourseInfo 类调用 English 类和 Chinese 类 获取学科信息,今后如果增加其它学科,就需要修改 CourseInfo 类,这样违反类DIP,高层策略没有和低层实现分离,抽象没有和具体细节分离,没有这种分离,高层策略就自动地依赖于低层策略,抽象就自动地依赖于具体细节。
/**
* 英语课程
*/
public class English {
public static final String NAME = "英语";
public void getClassTimeRange() {
System.out.println("早上:8:00 - 10:00");
}
public void getContent() {
System.out.println("学习英语语法");
}
}
/**
* 语文课程
*/
public class Chinese {
public static final String NAME = "语文";
public void getClassTimeRange() {
System.out.println("早上:10:00 - 12:00");
}
public void getContent() {
System.out.println("学习唐诗三百首");
}
}
/**
* 课程信息
*/
public class CourseInfo {
private English english = new English();
private Chinese chinese = new Chinese();
public void getCourseInfo(String courseName) {
if (English.NAME.equals(courseName)) {
english.getClassTimeRange();
english.getContent();
} else if (Chinese.NAME.equals(courseName)) {
chinese.getClassTimeRange();
chinese.getContent();
}
}
}
- 正确做法: 抽象出 ICourse 接口,English 类、Chinese 类、Math 类都继承该接口,而 CourseInfo 使用了 ICourse ,不管今后增加多学学科都无需修改 CourseInfo,并对于具体的实现类我们是不管的,只要接口的行为不发生变化,增加新的用户等级后,上层服务不用做任何的修改。这样设计降低了层与层之间的耦合,能很好地适应需求的变化,大大提高了代码的可维护性。
/**
* 课程接口
*/
public interface ICourse {
void getClassTimeRange();
void getContent();
}
/**
* 英语课程
*/
public class English implements ICourse{
@Override
public void getClassTimeRange() {
System.out.println("早上:8:00 - 10:00");
}
@Override
public void getContent() {
System.out.println("学习英语语法");
}
}
/**
* 语文课程
*/
public class Chinese implements ICourse{
@Override
public void getClassTimeRange() {
System.out.println("早上:10:00 - 12:00");
}
@Override
public void getContent() {
System.out.println("学习唐诗三百首");
}
}
/**
* 数学课程
*/
public class Math implements ICourse{
@Override
public void getClassTimeRange() {
System.out.println("下午:02:00 - 04:00");
}
@Override
public void getContent() {
System.out.println("背乘法口诀");
}
}
/**
* 课程信息
*/
public class CourseInfo {
public void getCourseInfo(ICourse iCourse) {
iCourse.getClassTimeRange();
iCourse.getContent();
}
}
- 总结:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体,具体应该依赖于抽象。