解读依赖倒置原则 。什么是依赖倒置?谁和谁的依赖倒置了?

什么是依赖倒置?

按照定义来说,它有两条:

  1. 高层模块不应该依赖于低层模块。两者都应该依赖于抽象。
  2. 抽象不应该依赖于细节。细节应该取决于抽象。

参见 OODesign

看定义似乎并不能解决“倒置”的疑问。

谁和谁的依赖倒置了?

定义的第一条说:高层模块不应该依赖于低层模块。两者都应该依赖于抽象。

这并不能解决“倒置”的疑问,因为它说的是该原则的代码层级的设计,它引入了一个抽象的中间层。

看第二句:抽象不应该依赖于细节。细节应该取决于抽象。

这里解释了倒置,从抽象依赖于细节的关系倒置为细节依赖于抽象

什么是细节,什么是抽象?

在低层模块到高层模块的代码设计中,高层模块的实现依赖于低层模块提供的功能。
业务需求作为抽象的事物依赖于有着细节的功能,只有低层模块完成了,高层模块才能被实现。
随着需求的增长与变化,功能越来越多,高层模块的代码会随着低层模块的变化而被不断地修改,以致越来越难以维护。

是时候改变了,怎么改变呢?

思考一下:

  1. 需求的实现是可以预见的,只要在需要的时候提供适用的功能即可。
  2. 这些功能的设定可以由高层模块来决定,由低层模块来实现。

这个时候高层模块就不再依赖低层模块的具体实现了,它只依赖自己对功能的设定。
可是低层模块怎么知道高层模块需要的功能是如何设定的呢?
这需要一个独立的约定,引入中间层就能解决。

看一个小剧场:

高层模块需要文件操作的功能,原来他都是自己干,后来越来越忙,就招人来干;
但是业务不断扩张,找到的人不能胜任工作了,他要么自己亲自下场培训要么亲自找人把原来的换掉。

到后来他不胜其烦,直到听说有专门提供人才寻访服务的猎头。
高层模块找到猎头说:我要文件操作的功能,帮帮我;
猎头:给我你的要求,我来找人。
高层模块:能读取、写入、覆盖、追加。
猎头:好的!低层模块甲,这个活你能不能做?
低层模块甲:可以。
猎头带着甲找到高层模块,然后交差了。
过来一段时间...
高层模块:当前我们只能进行本地文件操作,你能不能找个会远程文件操作啊?猎头。
猎头:可以
低层模块乙:等待多时了,嘿嘿。
猎头带着乙找到高层模块把甲替换了,然后交差了。

这个时候,高层模块对低层模块只管按照约定来差遣,不需要与之沟通。
当不满意或者希望找一个更有本事的文件操作员的时候,只需要和猎头沟通,由猎头来寻人。


看完小剧场,将其对应到代码中:

在设计业务代码的时候,高层模块不需要依赖特定的低层模块来设计,将要使用的功能抽离出来,并设定功能的设计标准,也就是由高层模块决定做什么。
而低层模块决定怎么做,它按照高层模块设定的标准来实现具体的功能细节,交由高层模块使用。

在 OOP 中,我们通过接口来定义功能,提通过类实现接口的方式来实现功能的细节。
这个时候即使还没有编写任何提供功能实现的低层模块代码,我们高层模块的代码也能完成。

这样的代码结构,实现了“高层模块不应该依赖于低层模块,两者都应该依赖于抽象”的定义。

而将业务需要的功能从业务代码中抽离出来,成为中间层,实现了从 “业务需求依赖于既定的功能” 到 “业务逻辑与既定功能的实现都依赖于抽象的中间层”的转换,对应了“抽象不应该依赖于细节。细节应该取决于抽象”的定义。

抽象依赖具体具体依赖抽象才是“依赖倒置”的真实含义,说的是“抽象和具体”的依赖关系,而不是原先的高层模块与低层模块之间依赖关系的变化,因为按照 DIP 原则设计代码结构后,二者已经实现了解耦。
过去我一直纠结这两个代码层的关系,而忽略了定义的第二句。光想着这俩都解耦了,没直接依赖关系了,还倒置个什么呀?想了好久才明白人家说的和我想不是一回事儿,观察角度错了。

概念不能凭空被解读,这些设计模式、设计原则是从实践中总结的,通过简单的描述或简单的示例代码并不能很好的理解,非得是实践才能出真知。

posted @ 2024-02-02 00:10  钰琪  阅读(89)  评论(0编辑  收藏  举报