在批判之前,先了解一下依赖倒置
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。
简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:
抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。
反面例子:
缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。
解决办法一:
将Light作成Abstract,然后具体类继承自Light。
优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据"开放-封闭"原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。
缺点:如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。
解决方法二:
优点:更为通用、更为稳定。
结论:
使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。
而我要说的是:要依赖于具体,不要依赖于抽象。
当然我并不是要批判依赖倒置原则本身,因为这个原则是正确的,但仅限于业务抽象分析领域,如果我们将抽象概念直接映射成代码则是不伦不类,只会阻碍生产力的提升并导致开发成本居高不下。我的意见是,业务抽象分析就留给业务专家们玩去吧,就算他们一眼就能发现所有人都能发现的light这一抽象概念,我们也不应该愚蠢到去写出class light或者interface light这样的东西。
不知道大家有没有看过robbin转载的《量子物理史话》,如果看过的话,就可以了解量子学说和相对论学说是如何颠覆经典物理学的。经典物理学虽然被颠覆,但并不是说经典物理学是错误的,它只是被请下了神坛,安分守纪的在自己的位置干好自己的事情,不复当年千秋万载一统江湖的风光日子。
如果我们学过化学,就应该了解H2O是什么东西,两个氢原子加一个氧原子,这是什么?分子,两种原子按照某种规律组合在一起而形成的分子,我们将之命名为“水”分子。“水”只是一个抽象名词,所以在我们的系统中,class water是不应该存在的,如果你非要表达这一个概念那么你应该这样写: Object o = new Object; o.Name="water"; why?为什么不应该写出class water这种东西?接下来我们要说一说量子学。
先举一个例子吧:我们的客户想要一个简单的订单处理系统,其中有一项功能需求是“订单录入”,我们将会这样描述这个功能:订单数据由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,两个氢原子加一个氧原子,水。物理学的发展史为我们揭示了现实世界精妙绝伦的结构,物理学的研究是收敛的,研究的越深,就会越来越逼近事实的真相和本质,而计算机科学研究则是发散的,每一个专家学者都可以异想天开创造出一个又一个的抽象体系,对软件人而言,到底是幸福还是不幸呢?
我要说的东西很杂,很多,也很乱,目前还无法成体系。
gigix你不如先说一说你所关心的业务逻辑问题,业务逻辑确实比较复杂,要把业务逻辑分解成有限的对象不是一件轻而易举的事。
这样吧,你来提业务逻辑需求,我来写代码。
我上面写的代码很重要,没有这些代码的支持是无法实现业务逻辑自动化处理的。
那么就先简单讲一下业务逻辑这种东西吧。
为了避免犯错误,把“业务逻辑”的概念弄错,google了一把“业务逻辑”,找不到关于“业务逻辑”的名词解释,“业务逻辑层”倒是出现了不少,同时出现的就是滥的不能再滥的“三层结构”,软件结构什么时候变得这么简单,只剩“三层”了。
最后还是找到一个解释:
引用
既然没人知道业务逻辑究竟是什么东西,那么接着继续说我们的例子。
上面我们已经实现了“订单录入”的功能,只要客户一按“录入”按钮,订单就会自动生成并保存。但是我们的客户并不满足,因为业务订单并不是可以随便乱填的,必须满足一定的条件才能建立,而这一个条件就是:a+b>c 以及 a-b
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)
当然这里出现的action和restriction只是一个概念示例,事实上它们是被各自分解为许多独立对象。
在上面的代码中我们可以看到 "((a+b>c) and (a-b)
看不懂没关系,正好证明你是一个不错的面向对象设计专家,这篇文章不会谈论如何使用设计对象原则、框架或者设计模式之类你所熟悉的东西。我要说的东西如果不是在对立面,也会是在另外一条岔道上。
先说一说终极目标吧
典型软件开发过程生命周期模型:
需求分析、设计(概要设计和详细设计)、编码实现、测试
终极软件开发过程生命周期模型:
需求分析、需求形式化、运行测试
如果终极系统拥有相当于优秀开发工程师的智慧和知识的话,需求形式化的工作也应该可以一并省略。
接着继续说我们的量子学,有些人或许会觉得奇怪,软件开发跟物理学有什么关系?相信大多数人都看过《matrix》,我们就姑且作一个大胆的假设,我们所处的世界正是一个matrix,但此matrix非彼matrix,这是由上帝设计的系统,这个系统非常庞大、非常复杂,人类所了解的知识不过是沧海一粟,我们将要设计的任何系统在这个系统面前根本就不值一提。什么叫简单设计?什么叫复杂系统?我们或许可以向上帝寻求答案。
与其他学科相比,物理学是最接近上帝的(请注意,此上帝非彼上帝,我们的上帝是爱因斯坦的上帝)。数百年来,无数的物理科学家孜孜不倦的做出各种尝试去探索世界的本质,归功于他们的不懈努力,我们才得以从量子学中窥探出上帝设计模式的一斑。
在人类眼中,这个世界总是斑斓多彩的,无数新奇事物等待着人们去发掘,去抽象,去给它们命名,去构造X工厂为它们分门别类。抽象,是人类的伟大发明,只要运用恰当,就可以将现象抽象为本质,并且可以将其命名为X模式。但是在上帝的眼里,世界要复杂的多,表面上毫不相干的事物,事实上有着某种直接或间接的联系,上帝制定了人类无法直接感知的规则控制着所有的一切,量子学则为我们抽丝驳茧的揭示了这些联系以及背后的规则。
软件复杂度的问题是结构复杂度的问题,上帝的matrix也面临着同样的问题,不过反过来说更为恰当,上帝的matrix面临的问题也是软件系统面临的问题,我们开发的软件是另外一个微型的平行matrix,之所以说是平行的,是因为我们的软件系统是对现实世界中的一小部分作出映射。
暂时先到这里,有闲情稚趣的人可以当科幻小说一样看看。
--------------------------------------------
gigix :
我也渐渐有点看明白了。也就是说,我们设计对象接口时就是在规定业务逻辑。但就像Martin Fowler说的,所谓“业务逻辑”就是业务中不讲逻辑的那些东西,所以我们的对象接口老是跟不上业务逻辑的变化。或者说,我们老是猜不到合适的接口。这个问题呢,TrustNo1也提到过。先听age0继续讲下去吧。
firebody:
我也明白一点了,
不过业务对象建模过程本身就是从具体--->抽象--->具体实现 过程,这个捕捉业务逻辑还是相对准确的。
难道,建模过程是先定好接口再做具体实现的???
我个人认为:
No! No! No!
首先是详细了解业务逻辑(详细用例分析),然后针对用例做这些分析,搭建对象框架,其实这个过程是一个很抽象的过程,也是最难把握的过程。按照我的经验和理解,我个人认为,对于较复杂的业务逻辑,我会针对它做一个草图设计,有一个画一个,画一个又一个主干用例的过程本身是一个迭代过程,感觉就是将具体用例过程 先做具体对象实现过程,然后再“综合”抽象这些具体对象实现到一个抽象层, 这就是我们的顶层类框架草图。
双引号 框上“综合”,这个词份量很重,因为在分析单独一个用例的时候,你是不知道哪些部分要做一个抽象或者设计,等到第二或者第三个用例分析,还发现同样的业务逻辑部分,你就会综合抽取这些相同的逻辑出来做一个抽象设计。
xiaoyu:
这点我很同意,不可能一开始就设计接口吧(我相信会有这种牛人,对所有的事都很清楚--god).
通过现象看本质.
---------------------------------------------
我觉得开始就接口不错啊.
遇到需求, 先写接口, 先写客户代码, 弄清楚自己想要什么, 比上来就实现强多了.
就算以后发现需求理解得不好, 需要改动, 用接口也不比直接实现麻烦.
关键是: 谁说你要对什么都清楚了才开始写接口?
接口就是类似一个描述需求的伪码而已. 有什么了不起的?
因为现在比较忙,写科幻小说也不是一件容易的事,所以连载的间隔时间可能会长一点,请大家见谅。但是主轴应该还是物理学,在恰当的时候也会稍微谈及文学、戏剧及电影,艺术实际上也是一门科学,因为大家都同处于上帝的matrix当中,直接或间接的联系是必然的。
但是在继续下去之前,先问一个问题,大家对于“接口”这个概念是不是有一个一致的共识,就具体而言,“接口”在各位的平行matrix当中指的是class?interface?还是别的东西?希望有一个比较一致的认识,否则谈到具体问题的时候又会演变成空对空的局面。
(注:平行matrix是一个很有趣的说法,平行是弦论的概念,弦论推论出平行宇宙的可能性,记得李连杰的《The One》吗,matrix则是《matrix》中对宇宙的另外一个更有趣的假设,更符合软件人的观点)
----------------------------------------------
Trustno1:
其实,这是一个非常简单的问题.没有那么复杂,也没有那么多神神叨叨的。
所谓的抽象,只不过是一种归纳法。归纳法只会对已经知道的事情起作用。
我们不可能说,我看到了一千只乌鸦是黑的,就断言第一千零一个乌鸦一定也是黑的。从我们经历过的事例推出我们没有经历过的其他事例,本来就是荒谬不羁,不合逻辑的.
所谓的需求变更,就是那些我们从未经历过的事情。如何对待这种东西?人类唯一的方法就是Guess&Gambling。Science is just Guess。抽象或者归纳法在这些东西面前是毫无用处的。对于需求来说,更为困难的是:大部分需求都是无法在人和人当中传递的,无法去确证的。就如我一直举的那个例子:我认为这朵花很美。对你来说,你不可能知道我这朵花美在那里,是颜色?气味?形状?或者是一种综合的体验?即使你知道,你也无法去证实。
我们很多人都是从理工科毕业的,所以特别相信。我们是可以认识这个世界的,我们是可以对这个世界进行形式化预测的。有些东西可以,有些东西却不可以。需求就是属于不能进行预测的一类。对于大多数的软件工程师来说,他们喜欢用好的结构去应付需求变化。这其中本身就犯了两个错。抽象,模式这些东西属于归纳法,刚才说了归纳法与预测需求本身就是牛头不对马嘴搭不上边的。其次,需求是否可以用一种形式化的东西去预测呢?我认为不能。我们最多能做的是Guess&Gambling。需求无法进行形式化的预测-----这不是需求的问题,也不是软件方法的问题。而是人类自身语言上的缺陷。如果你希望了解需求像演算物理方程式那样唯一确定,那么这将是一个可怕的世界——1984 forever。
PS:物理学从来不揭示这个世界的本质。物理学所揭示的只不过是这个世界上某些现象所呈现出来的数据关系而已。量子力学就是最典型的例证。量子力学从来没有揭示过微观世界的本质,他不知道他们观测到的到底是十么。量子力学只是说,他观测到的数据是十么,这些数据之间有十么关系。所以那些波动说,粒子说其实统统的都是胡说八道。微观世界到底是十么,没有人知道。以爱因斯坦从来不相信这套,因为他是一个确定论者,更加是一个本质论者。
从我的观点看,物理学所能解决的是How的问题,why的问题只能由上帝来回答。
----------------------------------------------
事实上并不仅限于物理学,人类所研究的任何一个学科都只是揭示这个世界上某些领域的某些层次中的某些现象所呈现出来的数据关系而已。每一项科学理论都必然是从现实世界中一系列具体现象所呈现的数据关系归纳演绎出来的,那么我们是不是可以说科学研究揭示的只是现象,而不是本质?
“微观世界到底是什么,没有人知道”,这句话很有趣,由此可以推导出并不仅仅是微观世界,无论是宏观世界还是什么观世界都没有人知道是什么,即使你看见的东西你实际上也不知道是什么,因为你所“看见”的影像只是你的视觉神经对物体反射光产生的脉冲信号而已,你所了解的仅仅是物体所呈现出来的光属性,你凭什么说你了解这个世界?仅仅是因为别人看到的东西与你相同?
为了避免无谓的争执,先学习一下科学本质。
引用
class Animal
{
string voice = "";
public void setVoice(string v);
public void Bark();
{
print(voice);
}
}
class Animals
{
static public Animal Cat();
{
Animal cat = new Animal();;
cat.setVoice("喵~~");;
return cat;
}
static public Animal Dog();
{
Animal dog = new Dog();
dog.setVoice("汪~~");
return dog;
}
}
client:
Animal cat = Animals.cat;
Animal dog = Animals.dog;
cat.Bark();
dog.Bark();
---------------------------------------------
基本上就是这样,不过最近我也修正了自己的观点,DIP本身是一种方法工具,工具本身并没有任何错误,运用工具的人才可能会犯错。DIP在系统设计层面非常适用,因为系统层面的抽象适用范围广、持续时间长,一个好的系统抽象将会长久的运用下去,但是在短促多变的业务层面则不大适用,高层次的业务抽象没有实际意义,低层次的业务抽象又无法灵活应变。
DIP与其说是设计原则,倒不如说是设计目标来得更加贴切。
DIP所追求的高层次泛化抽象模型只能是在广泛性的探索和研究中归纳总结出来,在一般性的个别开发过程所得到的信息并不足以归纳出足够泛用的抽象模型。而OO也仅是一种方法工具,并没有强制约束应该以何种方式进行归纳或演绎,OO的功劳只是能够以自然幽雅的方式表达及展现DIP抽象模型而已。
所谓“OO提供的是一个不断特化的过程,要求我们一开始就给出最一般最抽象的概念,然后不断的特化以求描述客观”应该算是对OO的误解,OO建议好的系统应该建构在合理的抽象体系结构之上,但是并没有强制要求一开始就建立优秀的抽象设计,任何人都知道这是不可能的,任何好的抽象都必然是建立在归纳的基础之上。