官方定义
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
高层模块不应该抵赖底层模块,两者应该依赖抽象
B. Abstractions should not depend on details. Details should depend on abstractions.
抽象不应该依赖细节,细节应该依赖抽象
场景
我们先以一个严重违反依赖倒置的实现排序功能例子来说明此原则的内容。某天小明的经理要求他说公司订单以Bubble方式来按创建日期来排序,小明接到任务后,马上投入工作,洋洋洒洒写出以下代码:
public class Bubble {
public void doSort() {
System.out.println("sorting by bubble");
}
}
package basic.oop.dip.sort;
public class Computer {
public void callSort() {
Bubble bubble = new Bubble();
bubble.doSort();
}
}
public class Client{
public static void main(String[] args) {
Computer computer = new Computer();
computer.callSort();
}
}
小明觉得运行无误,提交,交给客户,性能良好。
日复一日,订单数量以指数量倍增。客户向经理反应说订单排序超级慢,要求改善性能。于是经验丰富的经理给小明提供了好几种排序方案,要求小明整合到原先的程序。经理给了:
public class EfficientSortSolution1 {
public void doSort() {
System.out.println("sorting by EfficientSortSolution1 ");
}
}
public class EfficientSortSolution2 {
public void doSort() {
System.out.println("sorting by EfficientSortSolution2 ");
}
}
public class EfficientSortSolution3 {
public void doSort() {
System.out.println("sorting by EfficientSortSolution3 ");
}
}
然而,此时小明发现,自己写的Computer类无法执行经理给的3个方案。惨!只能去修改Computer。
public class Computer {
public void callSort(String sortType) {
if(sortType == 0){
Bubble bubble = new Bubble();
bubble.doSort();
}
if(sortType == 1){
EfficientSortSolution1 ess1 = new EfficientSortSolution1 ();
ess1 .doSort();
}
if(sortType == 2){
EfficientSortSolution2 ess2 = new EfficientSortSolution2();
ess2.doSort();
}
if(sortType == 3){
EfficientSortSolution3 ess3 = new EfficientSortSolution3();
ess3.doSort();
}
//...
}
public class Client{
public static void main(String[] args) {
Computer computer = new Computer();
computer.callSort(1);
}
}
小明加班加点改完,千辛万苦测试无误后,通知经理,经理说,客户提供了新的排序方法。要求小明在那个基础之上继续整合上去。小明内心:你。。mmp。毫无疑问,小明又要重新修改Computer类的callSort方法,加上新的solution,再重新测试。。。
小明犯了什么错误?
答案很明显,小明的程序严重违反了依赖倒置原则,即高层类严重依赖底层类,Computer严重依赖Bubble等排序类,这两个模块高度耦合,比如我们增加排序方法,势必导致Computer类的修改。一个系统的某一个模块发生变化,牵连一系列模块的连锁更改,不是一个合格程序员写出来的代码,而是我们眼中的垃圾代码。这样的垃圾系统的维护代价是非常巨大的。
依赖倒置粉墨登场
依赖倒置的解决方法是什么呢?我们看定义即知:两者必须依赖对象,并使用依赖注入的手段实现依赖的倒置
得到教训后,学习了依赖倒置后的小明写下,
public interface SortSolution {
public void doSort();
}
并修改各个Solutions,
public class Bubble extends SortSolution {
public void doSort() {
System.out.println("sorting by bubble");
}
}
public class EfficientSortSolution1 extends SortSolution {
public void doSort() {
System.out.println("sorting by EfficientSortSolution1 ");
}
}
public class EfficientSortSolution2 extends SortSolution {
public void doSort() {
System.out.println("sorting by EfficientSortSolution2 ");
}
}
public class EfficientSortSolution3 extends SortSolution {
public void doSort() {
System.out.println("sorting by EfficientSortSolution3 ");
}
}
最后修改“万恶”的Computer,
public class Computer {
private SortSolution solution;
public void setSortSolution(SortSolution solution) {//依赖注入实现依赖倒置
this.solution = solution;
}
public void callSort() {
solution.doSort();
}
}