2007
年
3
月
21
日星期三
第三章
发现对象
寻找和评定候选对象的步骤
详情见本书第
66
页,其实这些内容和好多方法论的思想差不多,核心不过就是“是什么”“做什么”“怎么做”的不同版本。这些步骤只是思考方法,并未指明工具,其基本思路其实很简单,就是先对领域进行描述(设计提纲),从描述中发现重要的主题(主题就是讨论问题的角度,比如从分布式式系统的角度来考虑需要什么对象,从业务角度考虑需要什么对象),再从主题中发现对象(描述关键概念的对象,描述软件外部特征的对象和描述附加机制和结构的对象),然后再细化对象,进而对已有的对象进行分析,寻找共性,进行进一步抽象和合并。最后一步很有意义,自问每个对象是否有存在的必要。
受到
Larman
对于用例的深刻理解的影响,我越发觉得文字是描述一个系统概貌很好的工具,而不是图。而且我也觉得
XP
中的
User Story
是很好的实践,这些用自然语言写出的东西很方便与领域专家和业务人员进行交流。但是所有这些都有一个统一的原则,就是简单!够用就好,不要写太多,而且以后可能会增加,一次写太多也理解不了,人的知识是需要一个满满的积累过程的,你一天能读完《史记》吗?一天读完之后你还能分得清司马相如和蔺相如吗?
找寻对象、角色和类
P67
我们建议你先寻找那些候选角色和对象。一旦你决定在设计中保留它们,再考虑使用接口还是类来实现它们。
我觉得上面这段话对我而言相当有启发,以前看
Rod Johnson
的《
J2EE
设计开发编程指南》时,对“实现依赖于接口,而非接口依赖于实现”觉得很疑惑,总是觉得我们应该设计好类,然后再将那些公共方法用接口的形式对外公开,怎么才能先把公共方法先写出来呢?不过现在终于明白这一顺序了。
实际上接口或是类的定义是通过角色和对象来的,它们的本质都是责任,角色是一组可以由不同对象承担的责任,对象通过承担责任来表现它扮演什么角色。而这些责任有些就会变成未来接口中的方法签名了。等于是说,我们先来讨论系统中有哪些角色,哪些对象,然后再考虑是通过接口还是类来实现它们,类是使用抽象类还是具体类。
直观的来看,我们最先能找到的对象肯定是领域对象。因此作者给出的步骤是这样的:
l
寻找第一批对象——代表系统中的概念、结构、机制等重要元素的对象
l
然后思考对象的共性,定义公共角色
l
然后将对象转化成类或接口。可以使用继承、抽象、接口和协作等手段来构造合理、灵活的设计。
很喜欢作者对于抽象类、具体类和接口的描述,作者对于面向对象核心概念的阐述都让我觉得很深刻,而不像阅读狠多高深的教材那样——不知道作者在说什么!
P67
抽象类提供了责任的部分实现,并制定了子类必须实现的责任规范
(或许说的是钩子函数或回调函数)。具体类提供了责任的完整实现,而接口使用一组方法签名(
method signatures
)更精确的定义了责任,但没有为其指定实现。
候选对象特征
作者接下来在“寻找的策略”这个子标题下介绍了候选对象代表的内容,我觉得这又是一份充满了作者经验的清单,它为我们提供了寻找对象的方法,它的意思就是说:“如果看到这些内容,那么就把它看成候选对象”。而且作者还强调了这份清单与领域另一份清单要契合,那份清单就是角色分类(角色构造型,我不太喜欢这个译法,它总让我想到构造器)。我将候选对象包含的内容叫做候选对象的特征:
l
系统完成的工作
(应该指的是领域概念,比如销售,付款等)
l
直接受应用程序影响或与其有关联的东西(其他软件、物理及期或硬件设备)
l
软件中的信息流
l
决策、控制和协调的行为
l
结构和对象群
l
对应用程序有意义,代表现实事物的对象。
候选对象应该指的就是领域模型,更准确地说应该是其中包含领域模型,我想在这里可以跟
Larman
的书中的方法做个统一,描述主题可以使用用例文本,而现在要做的就是发现领域模型或者说是候选对象。
我觉得作者的经验分享应该受到重视,下面摘录了作者对于某些应用应该选择的对象的经验:
l
如果应用程序的中心任务是计算
,那么将其分解为多个服务供应者对象,这些对象分别具有计算、转换和演算等功能。你或许还要设计出代表算法或控制工作流程的对象。
l
如果你的应用程序的主要行为是进行信息收集和传送
,则对象通常是信息及其传送操作的模型
l
如果应用程序与其它系统相连
,设计代表连接的外部接口则成为了工作的重点。大部分设计都需要进行控制和协调的对象,根据控制的复杂度,设计这些对象或许成为整个设计工作的主要部分。
l
如果应用程序需要彻底地分类、组织和建立相关对象间的联系
,那么就应该有构造者对象。
P72
在你所寻找的对象和应用软件所完成的工作的本质之间通常都会存在着某些直接关联。
作者这段或许是过去经验的总结,然而并没有在文中用特殊的字体来表示,我想我也应该按照
Rod Johnson
所说的“循证”原则,对这些进行一番实践再下结论
~
关于命名
我也一向主张,要给对象、方法、属性等赋以有意义的名字,作者在“”名字到底有何内涵,给出了好的建议:
1)
修饰通用名字。
也就是在通用的名字前面加上表示该特定对象的特征的修饰词,比如
Calendar
类表示日期和时间系统。
ChineseCalendar
自然就表示中国日历了。
2)
名字里只能包含将最有启迪性和最突出的因素
。意思是说不要把任何细节都写道名字里,要不然这名字也忒长了。
3)
给服务提供者以“
worker
”式命令。
并不是说名字后面都要用
worker
,而是说加上
er
,表示服务,比如
SystemClassLoader
等;如果某些情况下
er
表服务听起来不太舒服就用
Service
也行,比如
NamingService
4)
为那些名字暗示了广泛责任的对象寻找辅助对象。
5)
选择不限制行为的名字
。
6)
选择一个适合对象生存期的名字。
7)
选择一个适合当前设计背景的名字。
8)
不要重载名字。
9)
通过添加形容词来消除名字冲突。
10)
通过选择相似意义的名字来消除冲突。
11)
选择容易被理解的名字。
描述候选对象
本书作者建议通过确定对象的构造型来确定其意图,即找到一个对象,首先为它命名,然后用合适的一个或多个构造型来刻画它。
其中,服务提供者、控制者、协调者通过他们所完成的功能来进行区分,描述这些构造型的方法如下:
P79
服务提供者(或者控制者,或协调者)是完成某种功能的一类事物。首先,简单的描述这些功能。然后指出与功能相关的重要的、有意义的信息,或其协作者。
有一条有意义的建议是命名与熟悉的或其概念是广泛认可的事物关联起来,以便于不同的人对其进行理解。
连接候选对象
把完成相似任务或相互关联的对象放在一起,将其组织起来,这一步如何去做,作者给出了如下建议:
l
依据应用层
l
依据用例
l
依据构造型角色
l
依据邻域
l
依据抽象层
l
依据应用主题
保留和抛弃候选对象
做什么事情都不能过度,因此寻找候选对象也要适可而止,在某一个阶段,什么样的候选对象应该保留,什么样的候选对象应该抛弃,作者给出了一个列表。
保留的候选对象的特征:
l
为它起个好名字
l
定义它
l
确定其构造型
l
确定它可以支持某个特殊用例
l
确定它是体系结构中的一个重要元素
l
赋予它一或两个初始责任
l
理解其外在视图
l
确定其重要性
l
把它与相似的对象区分开来
抛弃的候选对象的特征:
l
所拥有的责任与你更认可的候选对象的责任重叠。
l
对象的责任、角色都比较含糊
l
对象所持有的功能存放于软件需求之外
l
对于软件系统来说无特别价值
l
对于软件实现来说似乎无足轻重或又太过复杂。