跳到主要内容

依赖倒置原则(Dependency Inversion Principle, DIP)

定义

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

  • 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
  • 抽象不应该依赖于细节。细节应该依赖于抽象。
img
img

简而言之就是“面向接口编程”。

核心概念

依赖接口而不依赖细节(实现)。这样做的好处就是系统更具灵活性。底层的实现不会对高层的调用产生任何影响,我们甚至可以替换掉原来的实现,新写一套实现。

接口是一份契约,约定了方法的功能,传入的参数,输出的值。高层只调用接口传入参数获得返回值,而实现层则实现方法,处理具体参数,执行某些任务,然后将结果返回给高层。这一切都是由接口来约束的。

实例

获取天气信息

img
img
  • WeatherService接口中规定了具体的方法,以及方法的参数、返回值等信息。
  • Client接收一个接口的实现,但是不管是哪个实现。
  • WeatherInfo是接口中约定的返回值。包括日期、温度、湿度、风力以及天气信息等。

这样做的好处就是,比如一开始我们用的是百度的天气接口。但是百度接口因为种种原因停用了,然后换成了新浪的接口。百度的天气API和新浪的天气API返回的结果肯定是不一样的。但是这里的Client不用关心这些,因为接口中已经规定了具体的返回值的格式,而对于API中数据格式的差异是由BaiduWeatherServiceSinaWeatherService来处理的。BaiduWeatherServiceSinaWeatherService需要解析天气API中的数据,然后再封装成指定的格式。而API中返回的其他的信息(比如未来N天的天气)则被实现类忽略。因为接口中规定的返回结果中不需要。

依赖倒置可以减少类与类之间的耦合性,降低并行的开发风险。

依赖倒置就好似我们现在的前后端分离式的开发方式,定义好接口后,前端按照定义的接口规范来调用接口,然后使用符合接口规范的模拟数据来调试功能。后端按照接口规范中的定义实现接口,返回数据。

依赖于抽象

以下内容摘自《敏捷软件开发:原则、模式与实践》

一个稍微简单但仍然非常有效的对于DIP的解释,是这样一个简单的启发式规则:“依赖于抽象”。这是一个简单的陈述,该启发式规则建议不应该依赖于具体类--也就是说,程序中的依赖关系都应该终止与抽象类或接口。

根据这个启发式规则。可知:

  • 任何变量都不应该持有一个纸箱具体类的指针或引用
  • 任何类都不应该从具体类派生
  • 任何方法都不应该覆写它的任何基类中的已实现了的方法。