有读者对草图中的控制类用法提出不同意见。为什么要一个实体类一个控制类呢?全部用一个控制类不好吗?可以的。实际上在绘制草图的时候可以参考本组织所采用的框架来决定控制类的用法。控制类的使用是最为灵活的一个用法,但由于是草图的关系,草图的目的是用最快速最简单的方法来把需求转化到设计,再加上我个人觉得设计时由底向上比自顶向下要好(不容易遗露关键信息),也符合抽象是从特性向共性演变的特点,所以我个人习惯是先把控制类划到最小,再透过框架来抽象,而不是一开始就考虑框架问题。 笔者刚才说了,草图代表了需求的实现,是一个细节的表露。接下来的优化的调整,就以此为基础。主要的输入:草图,系统架构,业务规则,补充用例规约,系统原型。主要的输出:调整后的分析模型,子系统,组件视图和部署视图(针对分布式应用而言)。
先说说分析模型的输出
调整分析模型目的。设计是没有标准答案的,这里笔者只能试图通过例子说明思路,不可能覆盖所有问题,需要读者自行体会了。或者提出问题,个别解答。调整分析模型的目的,是为了使之具有更合理的结构,更有扩展能力和适应能力,能更清楚的表达逻辑。什么样的结构是好的?OO会说,封装度高,耦合度低,接口(边界)清楚...然而这些都是原则问题,笔者无法回答什么是好的结构,因为在笔者看来所有结构都有其优劣,就象经典的23个设计模式里同时有该模式的优点与缺陷一样。有趣的是,笔者发现能量守恒定律真的是普适真理,同样适合软件,封装度越高,耦合度越低的代价通常是由结构的复杂度来替换程序的复杂度的。比如开发框架,例如Spring,其强大的扩展能力和极低的耦合度是由复杂的AOP和IOC模式为代价的,其结果是完整的程序逻辑被分截成很多不连续的片段,对很熟悉AOP和IOC的程序员可能不是什么问题,对经验不多的,真是难以理解了。实际上,要做的事情不会因为采用了某个结构而消失,只不过从程序中转化到配置文档或部署文档而已。因此关于什么是好的结构这个问题,请原谅笔者无法给出答案了,只有最适合的,没有最好的。只想提醒一点,受能量守恒定律的制约,谨防过度设计,还是那句话,要做的事情不会因为采用了某个结构而消失,它只是被转化了。因此请根据业务规则,补充规约中的要求,参考项目周期,成本,开发人员水平等等因素,评估结构调整的得失,得出最平衡的方案。
划分子系统。在本BLOG中曾经与网友rwyx讨论过子系统划分的问题,详细内容可以去看以武会友栏目《系统分析,业务建模,UML,RUP相关》中关于UC矩阵的讨论,由于内容比较多,这里只例举笔者的观点和方法:我不认为子系统应该是功能性的,这是UC关注的点,我认为是内在逻辑性的,所以只有在内部逻辑得以明确,也就是分析模型出来之后,才可能决定子系统。划分子系统依据于分析模型的结果。我的做法是先把分析模型做出来,然后尝试将分析模型中的对象放入不同的包(这里的确有经验的成份,
并不是一个个瞎试的,最初的依据还是来自业务用例,把一个业务用例当成一个包,在此基础上再改进)。这时会发现一些有趣的内容,比如,某个control类有好几个包都需要,比如,某个包中的某个Entity类被多达七八个其它包所引用,比如,某个bandage类要与分散在七八个包里的control类打交道...为了解决这些问题,尝试将分析类移到别的包,合并一些包,分拣出公有元素形成新包,也就是所谓的LIB等等。最理想的情况,那些分析类的所有依赖都在局限一个包里,或只与LIB有关,或仅通过一个bandage与其它包交互....到这时就形成了子系统的雏形了,剩下的工作,就是参考UI的要求,决定将哪些包合成一个更高层次的包,这个包包含了UI要求,由于基础来源于业务用例,基本上也会符合业务习惯要求,这个包就是子系统,取个合适的名字就OK了。
总结一下,大部分子系统划分是自顶向下的方法,我用的是自底向上的方法。如果构成底部组件级别的包已经耦合度很低了,再用它们来组合子系统就自由得多,尽量参考UI要求就是了,这是为什么在草图中我把控制类分得很细的原因。不过得提醒读者,通过分析模型划分子系统的方法是笔者自己独创的,尚未有其它文献资料的支持,仅供参考,慎用^_^
组件视图。将联系紧密,共同向外提供某种服务的分析类组合起来,形成一个组件。这个组件将有可能是被复用的。但组件视图不一定是需要提取的,笔者一般使用组件视图情形是在分布式,或与外部系统有交互的情况下才做。笔者对组件视图的使用是基于SOA思想的,一个组件就是一个WebService模块,这个模块有被复用的要求,有被独立部署的要求,如果没有这样的业务需求,就系统内部而言,笔者认为并无组件视图的必要,包图就足够了。并且Rose里的组件视图实在是...难用。那组件是如何形成的呢?笔者做组件是通过观察边界类而来,并且这个边界的两边都是系统(不同于界面有一边是人)。观察这个边界两边系统的交互情况,如果交互很频繁,并且涉及双方的多个对象,这时就要考虑组件了。用一个逻辑的组件名字,将内部被影响到的对象组合起来,透过这个组件来与对方系统交互。同时维护交互目的的单纯性(比如不要把取钱和开户放在一个组件里,这是两个不同的目的),一类目的一个组件。如果是分布式系统,还得考虑组件涉及到的对象可以被独立部署问题。提醒读者注意,笔者对组件视图的使用方式和理解也与一般UML教科书不同,目前也未有其它文献支持,仅供参考
部署视图部署视图划分出系统的网络拓扑节点情况。不过老实说我觉得Rose中这个视图不是太有效的。宁愿选择Visio来绘制节点图。部署视图笔者不多说,因为真正需要做部署视图的一般是分布式应用,一般也都需要企业级应用服务器支持,如Weblogic,Webshpere等,购买了这些产品的项目,自然会同时有拥有这些大公司的技术支持,直接请他们提供解决方案好了。
接下说说调整分析模型的关键点
以上是分析模型在调整过程中可能需要产出的一些内容。在这之前的过程都是程式化的,而今天的内容,则需要个人的经验和能力了。下面笔者尽量说明一些调整的关键点。 关键点之一:业务规则,尤其是来自补充用例规约中的全局规则
业务规则需要被评估,它们是普遍存在的?还是局部存在的?所谓普遍,是指这个规则在大多数情况下都会起作用。所谓局部,是指这个规则只在某种情形下才起作用。对于普遍的规则,需要在分析模型甚至架构上处理,而局部规则可以由后续的设计模型处理。普遍规则的例子:actor所有操作都应该被记录;actor存取资源时应当被授权;局部规则的例子:actor在下一次借书前没有逾期未归还的书,否则不能借阅。前两个普遍规则例子将反应到本篇的分析模型图里。后一个局部规则例子要到设计模型时再给出示例图。 也应当关注那些复杂的,可能将来会经常变化的那些规则。如果这种变化的可能是普遍存在的,应当在分析模型中给予关注,否则,可由设计模型来处理。例如,不论是借书的条件,可供查询图书的条件,借阅证有效条件....都是很有可能变化的,那么,可能需要在草图的边界类和控制类之间加入一个Factory,来保证一定的规则替换能力。但如果只有借书条件可能变化,这个Factory只需要加在借书控制器上,由设计模型处理就行了。
关键点之二:结构化和耦合度调整
不好的结构是网状结构,对象之间互相依赖。这样的结构藕合度高,扩展能力和适应性就差,改动程序时经常牵一发而动全身。例如草图中的图书、借阅证、借书蓝和借阅定单。好的结构是树状结构,对象之间的依赖是单向的,不交叉的。调整后的分析类图表示了这一转变。当然有时候并不是能够完全做到这一点,尽量做到,并防止过度设计。 关键点之三:交互集中点调整
若某一个对象的交互非常多,它与很多个对象都在交互,这个对象就是问题多发地带了!也就是所谓的critical chain,瓶颈...它应当被调整。由于笔者所用的这个例子较为简单,为了延续一直以来的示例,唯一可以被看作是Critical chain的就是界面了,虽然用界面来讲这个例子不太合适,但思路是可以借鉴的。调整交互集中问题的方法有,重新规划职责或增加冗余,或增加中间调合层等等....这个结果可参看后面的分析模型图。
接下来,是根据上面的讲述产笔者提供的一些例子。这些例子只可意会不可言传的,很难说明,能否理解就看读者个人在OO设计上的经验了。
分析模型原图:
? 针对业务规则:actor所有操作都应该被记录,分析模型所建立的一个解决方案。但绝不仅这一个方案。
类图:
交互图: