草图代表了需求的实现,是一个细节的表露。接下来的优化的调整,就以此为基础。主要的输入:草图,系统架构,业务规则,补充用例规约,系统原型。主要的输出:调整后的分析模型,子系统,组件视图和部署视图(针对分布式应用而言)。
这一篇拖了很长时间,除了懒之外,另一个主要原因是一直找不到思路。想归纳一下自己的设计经验,找到一个相对容易学习的办法,结果总是不得要领。终于不得不承认设计工作是一项创造性的工作,是没有办法用什么固定的流程,普适的方法来完成的。除了知识和经验之外,个人的悟性恐怕也是影响设计好坏的原因之一。这篇文章写得很费劲,发现归纳出与具体需求无关的通用的一些方法真的很困难。相信对读者来说这篇恐怕是到目前为止最难懂的一篇了,因为太多的东西是只可意会不可言传的。如果让读者觉得困难了,只能说声抱歉,我已经尽力了。市面上所有有关设计的书目,无非是讲UML,讲OO原则,讲设计模式..这些要不就是理论,要不就是方法论,要不就是针对某一问题领域的解决方案。而当我试图总结普适的实践方法时,却发现非常的困难。我尽力而为,但仍免不了带着个人色彩以及具体化。最后,只能希望通过讲解一些关键点以及例子来给读者提供一些思路,提供一些借鉴意义。至于一个通用的设计方法,我彻底放弃了,相信那是一个不可完成的任务。或者说以我目前的能力,还不足以总结出这样的方法。
书归正传,这一篇讲如何调整和优化上一篇的分析模型草图。请注意我的用词是调整和优化,也就是说,大部分工作都是基于已经完成的工作的。细心的读者可能会发现,我一直试图说明的是,需求,分析,设计这些工作并非那么神秘,而是有一个程式化的过程。而我正希望整个过程越程式化越好,也希望读者都能找到适合自己组织和项目类型的程式。软件工程,既谓工程,必能遵循而重复。只有这样才能降低成本,压缩进度,减少沟通,提高质量。可重复的才有意义。然而从现在开始,这个程式将不复存在,个人的作用开始登上舞台了。
上一篇给出的草图,基本上是不动脑子的。照搬了业务实体,每个实体前加了一个控制类,只用了一个界面,整个过程只是把用例场景又重新模拟了一遍。有的读者要问,既然没有任何的改变,又没有分析的过程,那么做这个工作不是白费力么?实际上不是的,虽然是一个很简单的草图,但是我们已经完成了80%的工作,同时也为后面的工作打下了非常好的基础。这个草图用最简单最快速的方式把用例场景转化成逻辑场景,代表了从需求到设计的演变过程。再接下来的设计工作,只要不丢掉这个草图中的信息,不论怎么设计都保证能够满足需求,将会省掉接下来大量的验证工作而放心的在设计上下功夫。从效果上来说,草图虽然不一定出现在最终的设计成果里,但它的意义是显而易见的。
有读者对草图中的控制类用法提出不同意见。为什么要一个实体类一个控制类呢?全部用一个控制类不好吗?可以的。实际上在绘制草图的时候可以参考本组织所采用的框架来决定控制类的用法。控制类的使用是最为灵活的一个用法,但由于是草图的关系,草图的目的是用最快速最简单的方法来把需求转化到设计,再加上我个人觉得设计时由底向上比自顶向下要好(不容易遗露关键信息),也符合抽象是从特性向共性演变的特点,所以我个人习惯是先把控制类划到最小,再透过框架来抽象,而不是一开始就考虑框架问题。
笔者刚才说了,草图代表了需求的实现,是一个细节的表露。接下来的优化的调整,就以此为基础。主要的输入:草图,系统架构,业务规则,补充用例规约,系统原型。主要的输出:调整后的分析模型,子系统,组件视图和部署视图(针对分布式应用而言)。
调整分析模型目的。设计是没有标准答案的,这里笔者只能试图通过例子说明思路,不可能覆盖所有问题,需要读者自行体会了。或者提出问题,个别解答。调整分析模型的目的,是为了使之具有更合理的结构,更有扩展能力和适应能力,能更清楚的表达逻辑。什么样的结构是好的?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的就是界面了,虽然用界面来讲这个例子不太合适,但思路是可以借鉴的。调整交互集中问题的方法有,重新规划职责或增加冗余,或增加中间调合层等等....这个结果可参看后面的分析模型图。
分析模型原图:
类图:
交互图:
类图:
交互图:
类图:
交互图:
一个组件图的例子,网上交费业务的组件视图
再次提醒读者,这些例子只是针对某个问题的解决方案之一,不可避免的带着笔者的个人经验色彩。这些解决方案不是真理,没有高下,甚至没有对错。目的只是为了提供思路,而非模板和教条。熟悉设计模式的读者可以从设计模式中找到很多解决这类问题的模式。读者不应该拘泥于研究笔者这些解决方案,这些方案仅是为了配合说明调整分析模型关键点而提供的示例而已,重要的是理解那几个关键点。
这一篇当中,笔者讲述了一些调整分析模型的要点,并举了几个例子。笔者不得不说的是,写这篇文章真的花费不少力气。设计是一种创造性工作,随环境而变,随要求而变,随设计师而变...要总结出一套方法来实在是困难。笔者只能就自己的经验写一些要点。诚然,这些例子不足以解决所有问题。目前估计没人能做到"统一设计方法",真到了那一天,软件就是工业化生产了。笔者希望通过这几个要点和例子,能够激发读者的思路,举一反三,而不是照葫芦画瓢(我相信想画也画不出来)。今天这篇同时也揭示了分析模型到底要做些什么。读者也可以体会一下,调整分析模型与原来没有分析模型时调整设计类相比哪个更容易些,工作量更小些。
接下来,要讨论如何转化到设计类的问题了。但是设计类是实现类,它必然与软件框架,系统架构,实现语言息息相关。所以下一篇将讨论软件框架和系统架构在UML里的表现方形式,再下一篇才讨论分析类到设计类的转化问题。敬请期待。