[u][i]这个系列是早前发布在部门wiki上的,引导组里的兄弟入门OOD,希望同样对刚刚走到OOD门前的同学有用。[/i][/u]
在继续探讨OOD的设计原则之前,我想先就OOD本身和相关的一些概念做些澄清,概念清楚以后再来看OOD的设计原则和我们现在的代码可能会把握的更好一点。
看我们有些模块的代码,最大的一个感觉就是:我们在用面向对象的语言写面向过程的程序。好多代码只是被聚集在类和方法中,没有被很认真地分析、抽象和封装。我举个例子说明一下一个简单的OOD过程吧,我们要写一个程序,模拟"把大象装进冰箱"这样一件事,步骤我们都知道:第一步,把冰箱门打开;第二步,把大象装进去;第三步,把冰箱门关上。在没有任何抽象的情况下,设计是这样的:
[img]http://dl.iteye.com/upload/attachment/151231/ea1f270d-24ef-34eb-97c7-3caf4a8cc050.jpg[/img]
这是典型的面向过程的做法,虽然我们有接口有实现,但是OOD的思想被丢掉了。这里只是用代码把文字需求重新描述了一遍,没有做任何抽象,所有的事情都被"大象冷藏器"这个"伪对象"给干了,我们系统中可以找出很多这种"对象"。那么OOD的到底做什么呢?我觉得是:[b]找出一组相互协作的对象,并为它们分配职责[/b]。所以第一步,我们先把"对象"找出来,如下图:
[img]http://dl.iteye.com/upload/attachment/151233/63005644-9f1b-390a-b9b4-12c166c52de9.jpg[/img]
需求中省略了主语,这里做了补充,张三开关冰箱,李四装大象。注意这里只是一些对象,通常来讲它们不是最终的抽象,我们通过分析把它们所属的"类"找出来:
1、大象是一种具体的东西,这里需求说的是装大象,但不排除将来会装狮子老虎,所以应该抽象一下,定名为"装载物"
2、冰箱也是一种具体的东西,这里需求说的是装到冰箱了,但不排除将来会装到洗衣机大衣柜里,所以也应该抽象一下,定名为"容器"
3、张三李四是具体的人,显然应该抽象出来,定名为"操作者"(不定名为"人"是因为机器一样可以完成类似的功能,"操作者"是更高的一个抽象等级)
抽象过后的类图如下:
[img]http://dl.iteye.com/upload/attachment/151235/fba60737-760b-38cd-a267-d4eb6014a2e1.jpg[/img]
接下来就是为这些类分配职责了:
1、操作员应该有一个打开容器的职责,一个关闭容器的职责,还有一个装载物体的职责
2、容器应该封装一个开关状态,要修改这个状态,应该提供打开和关闭两个方法
3、装载物在此处没有职责
于是我们得到了抽象过的设计:
[img]http://dl.iteye.com/upload/attachment/151237/6f81641f-24c3-3cf4-8c4b-20d5b925494a.jpg[/img]
在这个设计下,我们使用抽象隔离了容器可能的变化、装载物可能的变化,和操作员可能的变化。当出现新的需求"把老虎装进洗衣机"时,我们的物体装载器不需要变化,只是为装载物和容器添加新的实现而已。
这个例子多少有点滑稽,但是当问题出现在我们的系统中,你就不会觉得滑稽了,比方说昨天讨论MobileChargingServiceImpl时提到的EqHandleResponseService (Eq是对易趣的缩写),这是一个接口,接口通常是一种抽象,但是很难想象这个接口是对什么纬度进行抽象,或者是为了隔离什么样的变化;再比如MobileChargeLimitManageService接口中有个sumHalfAnHourTotalChargeAmount方法, 这显然也是一个没有经过抽象的方法。
抽象不但是遵循OCP原则的关键,从某种意义上说,也是面向对象设计的关键。我们使用Java做企业级开发的程序员必须要掌握这种方法才行。
在继续探讨OOD的设计原则之前,我想先就OOD本身和相关的一些概念做些澄清,概念清楚以后再来看OOD的设计原则和我们现在的代码可能会把握的更好一点。
看我们有些模块的代码,最大的一个感觉就是:我们在用面向对象的语言写面向过程的程序。好多代码只是被聚集在类和方法中,没有被很认真地分析、抽象和封装。我举个例子说明一下一个简单的OOD过程吧,我们要写一个程序,模拟"把大象装进冰箱"这样一件事,步骤我们都知道:第一步,把冰箱门打开;第二步,把大象装进去;第三步,把冰箱门关上。在没有任何抽象的情况下,设计是这样的:
[img]http://dl.iteye.com/upload/attachment/151231/ea1f270d-24ef-34eb-97c7-3caf4a8cc050.jpg[/img]
这是典型的面向过程的做法,虽然我们有接口有实现,但是OOD的思想被丢掉了。这里只是用代码把文字需求重新描述了一遍,没有做任何抽象,所有的事情都被"大象冷藏器"这个"伪对象"给干了,我们系统中可以找出很多这种"对象"。那么OOD的到底做什么呢?我觉得是:[b]找出一组相互协作的对象,并为它们分配职责[/b]。所以第一步,我们先把"对象"找出来,如下图:
[img]http://dl.iteye.com/upload/attachment/151233/63005644-9f1b-390a-b9b4-12c166c52de9.jpg[/img]
需求中省略了主语,这里做了补充,张三开关冰箱,李四装大象。注意这里只是一些对象,通常来讲它们不是最终的抽象,我们通过分析把它们所属的"类"找出来:
1、大象是一种具体的东西,这里需求说的是装大象,但不排除将来会装狮子老虎,所以应该抽象一下,定名为"装载物"
2、冰箱也是一种具体的东西,这里需求说的是装到冰箱了,但不排除将来会装到洗衣机大衣柜里,所以也应该抽象一下,定名为"容器"
3、张三李四是具体的人,显然应该抽象出来,定名为"操作者"(不定名为"人"是因为机器一样可以完成类似的功能,"操作者"是更高的一个抽象等级)
抽象过后的类图如下:
[img]http://dl.iteye.com/upload/attachment/151235/fba60737-760b-38cd-a267-d4eb6014a2e1.jpg[/img]
接下来就是为这些类分配职责了:
1、操作员应该有一个打开容器的职责,一个关闭容器的职责,还有一个装载物体的职责
2、容器应该封装一个开关状态,要修改这个状态,应该提供打开和关闭两个方法
3、装载物在此处没有职责
于是我们得到了抽象过的设计:
[img]http://dl.iteye.com/upload/attachment/151237/6f81641f-24c3-3cf4-8c4b-20d5b925494a.jpg[/img]
在这个设计下,我们使用抽象隔离了容器可能的变化、装载物可能的变化,和操作员可能的变化。当出现新的需求"把老虎装进洗衣机"时,我们的物体装载器不需要变化,只是为装载物和容器添加新的实现而已。
这个例子多少有点滑稽,但是当问题出现在我们的系统中,你就不会觉得滑稽了,比方说昨天讨论MobileChargingServiceImpl时提到的EqHandleResponseService (Eq是对易趣的缩写),这是一个接口,接口通常是一种抽象,但是很难想象这个接口是对什么纬度进行抽象,或者是为了隔离什么样的变化;再比如MobileChargeLimitManageService接口中有个sumHalfAnHourTotalChargeAmount方法, 这显然也是一个没有经过抽象的方法。
抽象不但是遵循OCP原则的关键,从某种意义上说,也是面向对象设计的关键。我们使用Java做企业级开发的程序员必须要掌握这种方法才行。