04.设计原则之依赖倒置原则

129 阅读3分钟

依赖倒置原则

定义

高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。
抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。

从稳定和变化角度来看依赖倒置原则

稳定不应该依赖于变化,二者都应该依赖于稳定
稳定不应该依赖于变化,变化应该依赖于稳定

所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。 在平时的业务代码开发中,高层模块依赖底层模块是没有任何问题的。实际上,这条原则主要还是用来指导框架层面的设计

简单来说,依赖倒置原则就是指:代码要依赖于抽象的类,而不是依赖于具体的类;要针对接口或者抽象类编程 而不是针对具体类编程。

在开闭原则那篇文章中,我们知道实现开闭原则的关键就是抽象化,如果说开闭原则是面向对象设计的目标的话 那么依赖倒置原则就是面向对象设计的主要手段。

为了让你更好的理解这个原则,我举一个简单的例子进一步解释

依赖倒置原则实例

实例说明:某系统提供一个数据转换模块,可以将来自不同的数据源的数据转换成多种格式 如,可以转换来自数据库的数据(DatabaseSource),也可以转换来自文本文件中的数据(TextSource) 转换后的格式可以是XML文件(XMLTransformer)、也可以是XLS文件(XLSTransformer)等。

第一版设计:

public class MainSys {
    private DatabaseSource databaseSource;
    private XMLTransformer xmlTransformer;
    private TextSource textSource;
    private XLSTransformer xlsTransformer;
    // ...
}

由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源 或者新的类型的文件格式,客户端MainSys都要修改代码,违反了开闭原则,现在使用依赖倒置原则对其进行 重构

第二版设计: 首先抽象数据源和文件格式

public abstract class AbstractSource {
}

public abstract class AbstractTransformer {
}

public class DatabaseSource extends AbstractSource {
}

public class TextSource extends AbstractSource {
}

public class XMLTransformer extends AbstractTransformer {
}

public class XLSTransformer extends AbstractTransformer {
}

修改第一版设计的MainSys类代码,在该类中依赖抽象

public class MainSys {
    private AbstractSource source;
    private AbstractTransformer transformer;
}

第二版的设计重点就是封装了两个抽象类AbstractSource和AbstractTransformer, MainSys类依赖这两个抽象类,不再像第一版依赖具体类,这样的好处就是需求再发生变化 MainSys这个类就不用再修改,我们只需要扩展抽象类的实现即可,同时也很好的满足了 开闭原则。

总结

依赖倒置原则概念是高层次模块不依赖于低层次模块。看似在要求高层次模块,实际上是在规范低层次模块的设计。
低层次模块提供的接口要足够的抽象、通用,在设计时需要考虑高层次模块的使用种类和场景。
明明是高层次模块要使用低层次模块,对低层次模块有依赖性。现在反而低层次模块需要根据高层次模块来设计,出现了「倒置」的显现。

依赖倒置的精髓,在于接口/抽象所有权的倒置,高层不依赖低层,其实需要把低层的能力/协议抽象成接口,并且接口所有权属于高层,这样就能真正做到对高层的复用,而不依赖低层的实现细节,如果只是高层依赖低层的抽象/接口,那不是真正的依赖倒置。
比如:应用程序不需要调用Tomcat/Spring这样的框架来实现什么交互,而是由Tomcat/Spring拥有协议,应用程序实现此协议(比如实现Servlet接口),来完成交互