论坛首页 Java企业应用论坛

依赖倒置原则(DIP)批判 -- 称之为本末倒置原则更贴切

浏览 46591 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-08-24  
在批判之前,先了解一下依赖倒置

依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。

简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。

反面例子:


缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。

解决办法一:
将Light作成Abstract,然后具体类继承自Light。


优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据"开放-封闭"原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。

缺点:如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。

解决方法二:



优点:更为通用、更为稳定。

结论:
使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。
   发表时间:2004-08-24  
算作批判是不合适。
曲解了dip的概念。

不过表明了一件事:
所谓扩展性,是指对已知行为的扩展。
0 请登录后投票
   发表时间:2004-08-24  
我没觉得有什么不妥!你的例子恰恰表明针对接口编程对于提高系统的可扩展性是很必要的。但是好的思想也的有好的设计者来贯彻,当抽象不彻底并且缺乏泛化能力的时候就是失败的设计,这样的设计有的时候甚至还不如传统编程那样清楚明白,因此搞设计的人必须非常强才行,不能像编程人员那样可以半瓶水。其实这很容易理解为什么,假如你请了个蹩脚的建筑设计师去设计楼房,轻者你会发现这个楼建好后居然没有厕所,楼里的人不得不去200米外方便,重者这栋楼是危楼,说不定哪天就垮掉了,会死人的。
0 请登录后投票
   发表时间:2004-08-25  
上文只是照抄别人的例子解说依赖倒置原则而已,我还没开始批判呢,各位不用着急。

依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。

而我要说的是:要依赖于具体,不要依赖于抽象。

当然我并不是要批判依赖倒置原则本身,因为这个原则是正确的,但仅限于业务抽象分析领域,如果我们将抽象概念直接映射成代码则是不伦不类,只会阻碍生产力的提升并导致开发成本居高不下。我的意见是,业务抽象分析就留给业务专家们玩去吧,就算他们一眼就能发现所有人都能发现的light这一抽象概念,我们也不应该愚蠢到去写出class light或者interface light这样的东西。

不知道大家有没有看过robbin转载的《量子物理史话》,如果看过的话,就可以了解量子学说和相对论学说是如何颠覆经典物理学的。经典物理学虽然被颠覆,但并不是说经典物理学是错误的,它只是被请下了神坛,安分守纪的在自己的位置干好自己的事情,不复当年千秋万载一统江湖的风光日子。

如果我们学过化学,就应该了解H2O是什么东西,两个氢原子加一个氧原子,这是什么?分子,两种原子按照某种规律组合在一起而形成的分子,我们将之命名为“水”分子。“水”只是一个抽象名词,所以在我们的系统中,class water是不应该存在的,如果你非要表达这一个概念那么你应该这样写: Object o = new Object;  o.Name="water"; why?为什么不应该写出class water这种东西?接下来我们要说一说量子学。

暂时写到这里,先让各位看官放放烟火。
0 请登录后投票
   发表时间:2004-08-25  
楼主是想说依赖倒置原则的不适合场合么?
也太卖关子了吧。
0 请登录后投票
   发表时间:2004-08-25  
先举一个例子吧:我们的客户想要一个简单的订单处理系统,其中有一项功能需求是“订单录入”,我们将会这样描述这个功能:订单数据由a,b,c组成,我们需要一个录入界面可以录入a,b,c,并且有一个录入按钮,当用户按下该按钮的时候订单数据应该保存到数据库里面。

好了,这样一个需求对各位来说简直就是小菜一碟,但是我并不希望各位为这种琐碎的需求去写无聊代码,我们的挑战是:你应该写一个系统,这个系统直接读入上面的功能描述,并产生执行代码或者直接解释功能描述来运行。你可以将上述功能描述生转换成为你所定义的形式化文件,以便于你的系统进行解释和分析。如果你对自己的系统很有自信,也可以直接写代码,但是记住代码量应该尽可能逼近我们的需求描述。

代码示例:
Data order = new Data();
order.Add("a");
order.Add("b");
order.Add("c");

Form form = new Form();
form.default_control = "TextBox";
form.controls.Add("a");
form.controls.Add("b");
form.controls.Add("c");

Entity entity = new Entity();
entity.table_name = "order";
entity.AddColumn("a", "varchar");
entity.AddColumn("b", "varchar");
entity.AddColumn("c", "varchar");

Action action = new Action();
action.name = "录入";
action.data_access_type = "insert";
action.data = order;
action.entity = entity;

form.actions.Add(action);
form.controls.Add("录入", "Button");

form.show();

在上面的例子中,我们的系统完全不关心自己在处理什么业务,业务概念对系统来说是毫无意义的,任何一个业务对象最终都会分解为系统对象,任何一个业务过程最终都会分解成为系统过程,对系统来说,系统元素的排列组合构成了世界的全部。H2O,两个氢原子加一个氧原子,水。物理学的发展史为我们揭示了现实世界精妙绝伦的结构,物理学的研究是收敛的,研究的越深,就会越来越逼近事实的真相和本质,而计算机科学研究则是发散的,每一个专家学者都可以异想天开创造出一个又一个的抽象体系,对软件人而言,到底是幸福还是不幸呢?
0 请登录后投票
   发表时间:2004-08-25  
还是不懂。麻烦你快点行不行呢?
另外,界面和业务逻辑是两码事,下次举个漂亮点的例子。
0 请登录后投票
   发表时间:2004-08-25  
我要说的东西很杂,很多,也很乱,目前还无法成体系。
gigix你不如先说一说你所关心的业务逻辑问题,业务逻辑确实比较复杂,要把业务逻辑分解成有限的对象不是一件轻而易举的事。
这样吧,你来提业务逻辑需求,我来写代码。
我上面写的代码很重要,没有这些代码的支持是无法实现业务逻辑自动化处理的。
0 请登录后投票
   发表时间:2004-08-25  
你继续讲好了,业务逻辑是大家每天都遇到的东西,不必多提。
0 请登录后投票
   发表时间:2004-08-25  
那么就先简单讲一下业务逻辑这种东西吧。
为了避免犯错误,把“业务逻辑”的概念弄错,google了一把“业务逻辑”,找不到关于“业务逻辑”的名词解释,“业务逻辑层”倒是出现了不少,同时出现的就是滥的不能再滥的“三层结构”,软件结构什么时候变得这么简单,只剩“三层”了。

最后还是找到一个解释:
引用

问:
看了很多资料,发现“业务逻辑”和“应用逻辑”出现的频率较高,但是两者往往被混用,弄得读资料的时候有些糊涂。按照我现在的理解,“应用逻辑”应该是和特定的应用有关,例如是“报帐”系统还是“图书管理”系统。那么“业务逻辑”又是指什么呢?是指“工作流”吗?还有“领域逻辑”,它是否就是指静态的“领域实体”?
我对这些概念的理解总是有点“似是而非”,没有掌握其本质的含义和相互的区别。希望版主或是哪位大峡能指点指点。小弟才疏学浅,希望能举点例子来说明。我感激不尽。

答:
业务逻辑是从业务来看,指的是某个具体的业务的流程; 应用逻辑是从实现业务的程序来看,指的是实现某业务的应用程序的处理逻辑。如果应用逻辑和业务逻辑完全吻合,那么你的程序在满足业务需求上,就是完美的了,但是一般来说,这只是理想情况。

领域逻辑应该指适应于某个领域的通用的业务逻辑。

个人理解


既然没人知道业务逻辑究竟是什么东西,那么接着继续说我们的例子。

上面我们已经实现了“订单录入”的功能,只要客户一按“录入”按钮,订单就会自动生成并保存。但是我们的客户并不满足,因为业务订单并不是可以随便乱填的,必须满足一定的条件才能建立,而这一个条件就是:a+b>c 以及 a-b<c 或者 b=c

修改我们的代码,记得我们的action吗,只需要增加一行代码

Action action = new Action();
action.name = "录入";
action.data_access_type = "insert";
action.data = order;
action.entity = entity;

// 增加
action.restriction = "((a+b>c) and (a-b)<c) or b=c";

action 和 restriction 是最难设计的,因为必须要考虑到分支处理等执行序列的问题,如果太复杂的话就会成为另一个设计语言,所以只能按照概率进行有限处理,仅仅捕捉在项目中经常出现的逻辑处理模式,对于特殊的处理则建议重载action及restriction手工写逻辑处理代码

当然这里出现的action和restriction只是一个概念示例,事实上它们是被各自分解为许多独立对象。

在上面的代码中我们可以看到 "((a+b>c) and (a-b)<c) or b=c",和需求是一比一等价的,restriction知道在哪里取出正确的数据,并进行相应处理,这就是“名字”的威力,之前的代码中我们反复使用“名字”为索引,所以restriction能够拥有自动化处理能力。如果我们在上文中的代码是: int a = 0; int b= 0; int c= 0; restriction遇到"((a+b>c) and (a-b)<c) or b=c"时就会不知所措。

另外在 entity 中我们将a,b,c都声明为varchar,所以条件"((a+b>c) and (a-b)<c) or b=c"是无法验证的,编译器也无法检测出这种错误,所以代码审查及全面的黑盒测试是必不可少的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics