1.2 抛弃依赖倒置原则

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yqj2065/article/details/78935183

1.2.2 依赖倒置原则(DIP)是什么

Robert C. Martin在1996提出DIP,并在几年后收入到他的著作《敏捷软件开发》中。随后,国内外有大量的基于DIP的文章、许多使用或介绍DIP的书籍。国内几乎所有都关于设计模式的书籍都介绍它。一个以其昏昏使人昭昭的东西,居然大行其道,简直令人匪夷所思。【所以,我对国内几乎所有设计模式的书,只会翻阅一下,说声 垃圾。】

1.可笑的第五步

依赖倒置?很多设计的初学者会问:“哪里体现依赖的倒置”?回顾项目1【为什么要你们自己先做一遍?我希望你们自己从代码中获得 清晰的认识】,整个项目可以出现下面的五步变化。

l        SortorTest->BubbleSort。客户依赖具体的服务BubbleSort,地球人都知道这不好。

l        SortorTest->IntSort<=BubbleSort。应用程序SortorTest遵循针对接口编程/抽象依赖原则,依赖抽象类型IntSort,而BubbleSort自然地依赖父类型。

l        【SortorTest->IntSort】。这一步是关键。如果需要将控制模块SortorTest设计成框架,可以将控制模块SortorTest和它必须依赖的抽象类型IntSort打包。控制模块SortorTest从应用程序(上层模块)变为框架(下层模块),为控制反转(Inversion ofControl)。最好能够提供JavaDoc,并且将方法名sort 改成soooooort。

l        Main->【SortorTest->IntSort】<=BubbleSort。(其他程序员)使用框架。

l        带main的BubbleSort,BubbleSort->【SortorTest ->IntSort】<=BubbleSort。与第一步“依赖倒置”。

的确,从第一步到第五步,SortorTest->BubbleSort变成了BubbleSort->【SortorTest】,依赖关系倒置了——依赖倒置就是从A依赖B,变成B依赖A。

但是,没有一个DIP的鼓吹者会承认这么简单的说法,因为没有人会为毫无意义的最后一步提出一个设计原则。DIP所言的倒置,本身就是胡扯,因此其鼓吹者只好随意发挥,如 《HeadFirst 设计模式》【国内的垃圾书,我不想列举。因为我不在意它们。】对依赖倒置的解说甚至是“倒置你的思考方式”。

在应用程序设计和框架设计中,抽象依赖原则/ADP均有重要作用。在应用程序的编程中,可以把ADP视为一种规劝或忠告;而在框架设计中,依赖抽象类型则是条例和军规。

本节以对排序算法进行测试为例,说明ADP的重要作用。测试例程将有五个步骤的变化,其中涉及单一职责原则、针对接口编程/ADP、分层架构的框架设计(控制反转)等。Robert C. Martin 于1996在一个专栏上发表了依赖倒置原则(Dependency Inversion Principle、DIP),该原则是一个错误的尝试,他希望将针对接口编程和控制反转纳入其DIP中,形成了一个思路混乱的、不知所云的原则。

扫描二维码关注公众号,回复: 3355258 查看本文章

2. DIP论文的错误

或许DIP最早的翻译文章就是我在CSDN上帖出来的,当时虽然觉得该文章含糊,但没有太多人提到DIP,我也就将它等同“针对接口编程”。现在很多人提到它,就得仔细研究一下。RobertC. Martin的那篇现在居然找不到链接的原文,该文奇葩到了错误到处都是的地步。

下面说明其论文的错误。

(1)起点就错了。

论文中写到为什么我要使用单词"倒置"(“inversion.。坦白地说,这是因为比较传统的软件开发方法——例如结构化分析和设计,倾向于创建这样的软件结构:高层模块依赖于低层模块,并且抽象依赖细节。的确,这些方法的一个目标在于定义一个子程序层次以描述高层模块如何调用低层模块,....。因此,一个设计良好的面向对象程序的依赖结构,对应于传统的过程式方法通常会形成的依赖结构,是"倒置"

这暴露了该论文的立论基础已经错了。传统的过程式方法可以设计被调的函数库,也可以设计框架;设计良好的面向对象程序,可以设计庞大的工具箱类库,也可以设计更多的框架。在设计工具箱时,过程式和面向对象依赖结构一样;在设计框架时,过程式和面向对象依赖结构一样。

如果有人把女人中的好人和男人中的坏人加以比较,得到结论:女人与男人在人性上是倒置的,显然是极其荒谬的。在他眼里,设计良好的面向对象程序都是框架,而传统的过程式程序都是被调的函数库。可见,其论文的"倒置",从开始就是错误的。

框架=控制反转(Inversion of Control),框架设计,或者说Inversion ,与系统是不是采用“传统的软件开发方法”、“过程式方法”、“面向对象”,一点关系都没有。所以,其论文后面只得将错就错,但再怎样解释,只会使人不解。正因为DIP所言的倒置,本身就是胡扯,因此其鼓吹者不得不随意发挥,如《Head First 设计模式》对依赖倒置的解说甚至是“倒置你的思考方式”。

(2) 含混的高层-低层模块(High -low level modules)。

在介绍针对接口编程/抽象依赖原则时,不管采用什么术语,即不管是高层模块、控制模块或客户/Client,依赖者都不应该依赖“具体的”低层模块、步骤模块或Server,而应该依赖抽象类型IServer。使用控制模块通常意味着在框架场合讨论问题;而不论在分层场合,同层中或应用程序都使用C-S来进行一般性的讨论。

RobertC. Martin使用高层-低层模块,为什么他既不愿意将高层-低层模块等价于C-S,也不愿意使用分层架构中含义清晰的上层-下层?这种含义不清晰的术语最适合浑水摸鱼。

(3) 强说反转。

Robert C. Martin如何介绍其反转呢?首先写到:Copy()模块,它包含高层策略,依赖于它控制的底层细节性模块,再依赖抽象类型后写到:然而Copy类根本不依赖"Keyboard Reader"和"Printer Writer"因此依赖性被反转了!换言之,Client依赖具体类Server变成依赖IServer就是他的所谓“反转 。并陈述依赖倒置的一般形式:

依赖倒置原理、DIP

1. High level modules should not depend uponlow level modules, both should depend upon abstractions.

2. Abstractions should not depend upon details,details should depend upon abstractions.

这就相当于,实验1在介绍第2遵循针对接口编程/抽象依赖原则时,他就将没有发生的,对第3步框架的描述“反转”提前使用了。

(4)高层的复用性。

Robert C. Martin为了其原则,预设了一个前提,高层应该设计成框架。他写到:高层模块应该优于低层模块。...当高层模块不依赖低层模块时,高层模块可以被十分简单复用。正是这个原则,它是框架设计(framework design.)的核心。

在SortorTest->IntSort例子中,高层模块SortorTest的确具有作为框架的可能性;但是Robert C. Martin自己给出的例子,Copy模块,Java程序员都知道,几乎没有人为特定的应用开发IO框架,而是使用Java的IO工具箱构造各种各样的应用。在大多数场合,所谓的高层模块通常不具备复用性。

(5)依赖从未反转。

不论在应用程序场合,还是框架场合,正确的设计都是SortorTest->IntSort。控制反转/IoC中的反转,指软件设计决定的控制权,由应用程序员转移到框架设计者手中。Robert C. Martin的反转,指Client从依赖具体类Server变成依赖IServer。所以,他的反转,既不能按照IoC的反转理解,也不能按照日常的反转——颠倒来理解。如果程序员习惯了依赖抽象类型,Robert C. Martin的反转永远不会出现。

(6) 东施效颦的倒置/反转

Client从依赖具体类Server变成依赖IServer,被Robert C. Martin称为反转。他自己也觉得不好意思。于是,他需要在分层结构中,将他的反转伪装成控制反转/IoC中的反转。他的反转于是出现3中含义:对传统的过程式方法的反转、对依赖具体类的反转,IoC的反转。他的反转随时变化,想怎样解释就怎样介绍。因为他都不知道应该如何解释反转。

(7) 什么是抽象-细节

Robert C. Martin的DIP中,最令人困惑的是他不说人话。什么是抽象-细节?如果读者认为抽象-细节是父类型-子类型,那么“抽象类型IServer不应该依赖子类型Server,Server应该依赖IServer”就是毫无意义的废话。抽象-细节其实模仿针对接口编程,而不是针对实现编程中的接口与实现。前面介绍过,“针对接口编程”中提到接口和实现,容易使人不适当地联想到Parnas原则/接口与实现的分离原则。“针对接口编程”中提到接口和实现,应该理解为Java抽象类型(含Java接口和抽象类)和实现类。

而Robert C. Martin在模仿针对接口编程,而不是针对实现编程”的同时,他又搞不清与Parnas原则中接口与实现的不同。他写到:C++中,实现并不是自动与接口相分离的』,应用的高层(policy)没有与低层模块相分离;抽象没有与细节相分离。没有这样的分离,高层自动的依赖于低层模块,并且抽象也自动的依赖于细节』,说明他在想应用Parnas原则,但是Parnas原则中,可以说具体类的接口(抽象)没有与实现(细节)相分离,但是从来没有“抽象也自动的依赖于细节”的说法,只讨论接口与实现分离还是不分离,从来没有接口依赖还是不依赖实现的说法。

所以,Robert C. Martin的抽象-细节,既不是针对接口编程,而不是针对实现编程”的接口与实现,也不是Parnas原则中的接口与实现。从两套接口与实现中,你都无法明白Robert C. Martin想表达什么。

(8)Robert C. Martin 还捏造了一个 SOLID 作为面向对象设计原则 ,D 就是 DIP 提起来就恶心。




草稿 抛弃依赖倒置原则 将被修改为吐槽版。

猜你喜欢

转载自blog.csdn.net/yqj2065/article/details/78935183
1.2
今日推荐