OO系统设计师之路--分析模型
 

2009-05-04 作者:coffeewoo 来源:itpub.net

 

(1)--什么是分析模型

分析模型是采用分析类,在系统架构和框架的约束下,来实现用例场景的产物。 分析模型是高层次的系统视图,在语义上,分析类不代表最终的实现。它是计算机系统元素的高层抽象。

笔者认为分析模型正是OO设计的核心,而设计类只是OO的实现手段。

分析模型是MVC模式的经典应用。对比分析类的名称,MVC模式,读者应该能够发现分析类在OO和商业目标中精妙的对应关系:人,事,物,规则--actor,boundary,engity,control。这就是为什么笔者说分析模型是OO设计的核心。

(让关心OO之路列的朋友们久等了,今天正式开始推出之路系列的第二部,OO系统设计师之路。在第一部OO系统分析员之路中,我们始于什么是用例,结束于需求规格说明书。我们还记得在第一部中,最后的结果是系统用例。系统用例规定了系统范围,通过用例场景,规定了系统蓝图,让我们知道了系统将如何实现业务用例中规定的业务。这些工作,是由系统分析员来完成的。到这里为止,我们还不知道如何让计算机来执行这些业务。大家都知道,在需求过程结束后,即将进行的是分析设计过程,这是系统设计师的职责。OO之路第二部正是针对系统设计师的,笔者将试图在接下来的文章里,说明如何做系统设计,要运用哪些工具,产生哪些结果,以及如何来验证我们的设计是否正确。

这是设计师之路的第一篇,笔者要讨论的是分析模型。我们经常会听到分析模型这个词,但真正懂得,或者用过分析模型的人却少之又少。下面笔者将写下一段对话,这段对话是笔者在招聘设计师的过程中与许多应聘者对话的场景模拟。90%以上的应聘者都不能很好的回答这些问题。读者也可以试着回答,看看你对用UML进行OO系统设计有多深的了解。

对话场景:

-在需求过程结束以后,接下来你会做什么?
-分析设计
-你的设计依据是什么?
-需求结果,用例模型
-你是如何设计的?设计的结果是什么?
-设计类图,确定类的方法和属性,会用时序图来表达类之间的交互。还有会应用设计模式来增强系统的扩展性和复用能力。
-那你是如何确定出类来的呢?比如针对一个特定的需求,为什么你决定用三个类而不是五个类?类方法又是如何确定的呢?为什么设计七个方法而不是十个方法?
-短暂沉默后:经验啊,从我多个项目的设计经验和实际情况来看,用这几个类和这些方法完全可以满足业务要求,并且是经过优化的,是最好的方案。
-那么你如何能够保证,或者,你用什么来证明,你的设计能够满足需求呢?除了经验之外,你能用什么方法来证明呢?
-沉默之后:我有很丰富的设计经验,我的设计是经过深思熟虑的。设计会经过评审,讨论和充分的沟通,后面还有测试,不满足需求时会再进行修改和补充的。
-刚才你也说了,需求结束后你将进行分析设计的工作。你能说说分析和设计的差别吗?分析做什么,设计做什么?
-更长时间的沉默:分析和设计是同一个过程,分析是想的过程,设计是把想的内容用类表达出来。
-我们知道在UML里有分析模型和设计模型,如果分析和设计是同一个过程,你能说说分析模型和设计模型的区别吗?
-沉默...

是的,的确是这样。很多人分不清分析和设计不同的目标,没有使用过或根本不知道分析模型。更多的人无法回答他的设计如何能够被证明是满足需求的,那些类在他们看来,都是凭经验,如同精灵一般从脑子里蹦出来的。他们很自信自己的经验和设计能力,津津乐道于一个又一个设计模式,他们认为,如此优秀的设计怎么会不满足需求呢?证明?很奇怪的问题,我设计的目的就是为了满足需求,不满足需求的设计我会不断改进啊,最终它一定是满足的啊。

可惜这并没有回答我的问题,我问的是如何证明,而不是是不是满足。即使设计师拥有丰富的经验和超强的设计能力,设计结果的确满足了需求,并且很优秀,但那只是结果而不是过程,那是个人英雄的胜利,而不是软件过程的胜利。

事实上,这一切问题,都只是因为设计师们遗忘了很重要的一个过程,分析模型。那么什么是分析模型呢?为什么分析模型能够解决这些问题呢?

RUP里对分析模型和分析类的定义是:分析类用于获取系统中主要的“职责簇”。它们代表系统的原型类,是系统必须处理的主要抽象概念的“第一个关口”。如果期望获得系统的“高级”概念 性简述,则可对分析类本身进行维护。分析类还可产生系统设计的主要抽象:系统的设计类和子系统。

如果你对上面的定义感到迷惑不解(RUP的定义一向如此),下面是笔者在实际工作中对分析模型的理解和应用经验,或许可以帮助读者理清头绪。  

  • 分析模型是采用分析类,在系统架构和框架的约束下,来实现用例场景的产物。在OO之路的第一部里,我们说用例和用例场景规定了业务范围和要求,如果分析类完全实现了这些用例和场景,我们就能肯定的说分析类已经满足了需求。

    分析类是什么呢?一般来说,分析类有:

    边界类(boundary)


    实体类(entity)


    控制类(control)

    再加上实现用例场景需要的actor类,共四种。这些分析类将在后面的文章里详细讨论,读者现在只需要记住它们的样子。

  • 分析模型是高层次的系统视图,在语义上,分析类不代表最终的实现。它是计算机系统元素的高层抽象。上述的四个分析类可以完全模拟计算机的执行过程。分析类具化以后产生真正的实现类,即所谓的设计类,也就是大多数设计师所说的类图。
  • 笔者认为分析模型正是OO设计的核心,而设计类只是OO的实现手段。还记得上一部第一篇中笔者提到的复用的三个层次吗?组件级的复用实际上是通过分析模型表达的,而设计模型中的复用,只是利用了OO语言的特性,来实现复用的要求。
  • 分析模型是MVC模式的经典应用。从分析类的名称就可以看出来。笔者在上一部第四篇中讲到的一个观点:“商业系统无论多复杂,无论什么行业,其本质无非是人,事,物, 规则。人是一切的中心,人做事,做事产生物,规则限制人事物。人驱动系统,事体现过程,物记录结果,规则则是控制。无论OO也好,UML也好,复杂的表面下其实只是一个简单的规则,系统分析员弄明白有什么人,什么人做什么事,什么事产生什么物,中间有什么规则,再把人,事,物之间的关系定义出来,商业建模也就基本完成了。”。根据这一段话,再对比分析类的名称,想想MVC模式,读者应该能够发现分析类在OO和商业目标中精妙的对应关系:人,事,物,规则--actor,boundary,engity,control。这就是为什么笔者说分析模型是OO设计的核心。

问题是为什么要用分析模型呢?现在绝大多数系统的产生过程并没有用分析模型,不也照样运行?而且在RUP里,分析模型也只是一个Optional的过程,并非强制过程啊?

的确如此,这可能也是为什么大多数设计师并不用分析模型的原因。但是,同时,文章前面的对话场景中的问题也产生了。有读者要问,你说分析模型完全实现用例场景因此可以证明设计满足需求,那么我用设计类来实现用例场景,不也可以同样证明? 那分析模型又有何用呢?

而这正是分析模型要存在的原因。刚才,笔者说了,分析类不代表实现,它具化成设计类以后才是实现。分析类是系统元素的高层抽象。有经验的设计师,特别是那些擅长于使用设计模式的设计师们都知道,OO系统要保持扩展能力,复用性要好,要把变更影响控制在小范围内,就要应用高层次的抽象,用高层次的抽象接口来表达系统行为,而把具体实现delay到子类,配置文档,甚至运行期去。所有的设计模式,不论采取了怎样的技巧,均是为了这些目的。分析模型对系统设计来说也同样延续了这样的思想,用四个高度抽象的分析类来表达系统行为,而把实现delay到设计类中去。这些抽象透明于实现方式,也透明于实现语言,它表达的核心观点是系统架构,业务实现模式和规范,需求可回溯的验证。

比如,我们用一个实体分析类表达了某个业务实体,在分析模型中我们定义了所有针对该实体的交互和存取操作,对分析模型这个层次的抽象来说已经完整表达了计算机系统对业务需求的模拟实现。但其实这时并未真正的实现这个业务需求,一直到具化成设计类后,根据开发语言特性,框架,规范等等要求,这个实体分析类可以被具化成一个或多个SDO,POJO,EntityBean,可以用Hibernate,可以用 Webshpere BO,也可以用Weblogic XMLbean...等等。完全可以根据实际需要来确定实现方式和语言,因此实现得以delay,这就带来了OO扩展和复用的潜在能力。并且这时设计师已经不用担心具化出的设计类是否会脱离需求,它们已经在分析模型层次被验证,而能专心考虑系统实现所要求的那些特性。在后续的文章里笔者还会更详细的讨论这些问题,这里只是举一个例子。

为什么要用分析类而不是设计类去验证需求呢?这是由于抽象层次更高,分析类比设计类验证需求的工作量以及可能的变化都要少很多。比如针对登录要求,如果用分析类来表达,我们只需要向登录control类发一条登录请求就OK了。而设计类由于与实现方式相关,并且已经具化到了实现,所以根据安全验证方式不同,LDAP,CA,SSL,或应用服务器不同,登录方式和方法都不同,并且可能是很多个步骤,例如getUser(),getRole(),getGroup(),register()...你愿意用这么多说明才能表示已经验证了一个简单的登录要求吗?慢着,如果更换了安全模式呢?更换了应用服务器呢?这在现实情况中也很常见,对分析类来说,由于抽象层次高于实现方式,因此继续有效,而设计类却必须更改。这就是为什么要用分析模型来验证需求的原因之一。

较真的读者或许会说,安全模式变了,就算不为了验证需求,设计类本身也不得不改,这看起来没什么必然因果关系啊?考虑一下,在一个项目组里,当一份设计文档被share到负责各个摸块的开发小组时,各小组对该文档都形成了一个共同的认识。如果这个认识是基于分析模型而不是设计模型的,当安全模式改变,对负责安全模块的开发小组来说,他可以改变他负责的设计类而无需通知其它小组。因为从分析模型的层次来看,一切都没有改变。 这与大家熟悉的设计类中更换了类实现而保持接口不变的道理是一样的,只要接口不变就无须通知任何人。 而如果这个共识是基于设计模型的,一点小的改变都需要通知到各个小组,因为各小组的认识是基于类名,方法名的,改动了,能不通知他人么?从OO角度来说,这就是松藕合和紧藕合的差别。

从上面的例子可以看出分析模型比设计模型要稳定得多,因此用它来验证和表达系统到需求的映射是很好的。这有助于在开发过程中当实现类变来变去,一个类改两个,突然又加了一个设计模式(这种情形非常的常见吧?)时,保持系统到需求映射的稳定,同时也就保持了一个稳定的系统视图和业务架构。对开发小组来说,不会因为这些变动影响到他们对系统整体的认识。

分析模型较高的抽象层次有助于让人们更容易理解系统行为。由于与实现无关,因此可以用大白话来表达系统交互过程,比如对于登录要求,我们可以直接用“登录()”来表示这个系统请求,相比于设计类中的getUser(),getRole(),getGroup()之类的方法名,分析模型明显要直白得多。而开发人员对系统行为良好的理解显然会对开发有着很大的帮助。 在一个项目比较复杂,而且是多个Team横向合作的情况下,分析模型显得更加有效。因为它的简洁和稳定,会让各个开发组减少细节沟通的成本。

最后,如果你所做的项目并非一次性项目,而是基于一个行业,不断的有相似或相同的单子,更打算长期立足于这个行业,做精做深,形成完整的行业解决方案的话,分析模型更是必须要考虑的。在你的组织面对同行业不同客户时,可能无法保证所有客户都选择同样的语言,同样的软件平台,有着同样的业务要求。在设计模型的层次上,这必然导致很多个不同的实现版本。面对这么多不同的版本,如何去维护一个“统一的行业解决方案”呢?正如上面所述,由于分析模型抽象层次高于实现方式和实现语言,你可以用分析模型来维护这一个方案。还是登录的例子,虽然客户们可能有的用LDAP,有的用CA,将来可能还有新的模式,但对于分析模型来说,安全模块始终可以保持一致。这将非常有利于随着各个项目的进行,对分析模型的逐步维护,而形成一个统一的,稳定的架构和行业解决方案,而针对不同的客户要求,又可以提供不同的实现包。

这是OO系统设计师之路的第一篇。笔者讨论了什么是分析模型,以及为什么要用分析模型。下一篇将用一个示例说明分析模型如何做,它的结果是什么样的。敬请期待。

(2)--怎样做分析模型

分析模型是系统的高层抽象,是高于实现语言和实现方式的。因此在做分析模型过程中,要跳出固有的java思维,C++思维,同时也暂时不要考虑设计模式的应用,而专心的,用OO思维把四个分析类的职责和交互,以及它们之间的关系定义清楚。如果说用例分析大部分情况下是程式化的(笔者正希望它是程式化的),那么你会发现,分析模型大部分工作也是程式化的。

拖了很长时间才写第二篇,为自己的懒惰羞愧一个:P

上一篇笔者阐述了什么是分析模型,我们为什么要使用分析模型,分析模型能给我们带来什么。这一篇来讨论怎么做分析模型。

开篇之前先说点题外话。笔者不厌其烦地一次次提到需求的可追溯性,是因为软件工程比UML更重要更本质。笔者自己在学习UML过程中,曾经也非常迷惑而不得要领,这么多UML元素,每个都有其特定的含义,RUP中定义了更多更复杂的流程,模板,工具...虽然读了很多资料,却始终感觉UML信息太过于分散,不能很好的把UML应用到实际的项目中去。直到有一天突然转变了思维,不是从UML的定义中去思考如何做软件,而是站在软件工程的角度,去UML中找寻需要的工具。正是这一转变使我对UML的认识茅塞顿开。我想,初始学习UML的人可能也会经历跟我同样的困惑,在这里我愿意把我的领悟与大家分享。对软件项目来说,OO也好,面向过程也好,UML也好,UC矩阵也好,这些都不是最重要的,软件项目真正的灵魂是软件工程。软件工程的需要才是这些工具诞生的原因。因此我建议阅读我文章的朋友们,在讨论如何应用UML之前,应当先系统学习软件工程。只有掌握了软件工程,你才会知道为什么要有用例,为什么要有分析模型。站在软件工程的立场,那些孤独的UML图符才会变得有生命力,你随时都会知道需要用什么样的UML图符来表达软件的观点。UML也不再面目可憎,它们是一群有着强大能力的精灵,帮助你在复杂的软件工程道路上搭起一座座通向光明目标的桥梁。

虽然是不厌其烦,还是要再次提醒,注意需求的过追溯性,这是软件工程的需要。这一篇我们要讨论的话题里,仍然逃不开这条主线的牵引,你会发现这一篇里产生的任何一个成果都与之前的工作息息相关。如何做分析模型?在UML里,RUP里没有明确的答案,我从软件工程中找到了答案,正如上一篇所说,析模型是采用分析类,在系统架构和框架的约束下,来实现用例场景的产物。用例场景是什么?是用户需求的模拟,实现用例场景,就是实现需求。为了达到需求可追溯的目的,分析模型需要以下这些输入(还是采用用例分析系列文章中的例子):

  • 用例场景
  • 与用例实现相关的领域模型
  • 用例规约 以及
  • 补充规约

在正式开始做分析模型之前,笔者有必要提醒一下,分析模型是系统的高层抽象,是高于实现语言和实现方式的。因此在做分析模型过程中,要跳出固有的java思维,C++思维,同时也暂时不要考虑设计模式的应用,而专心的,用OO思维把四个分析类的职责和交互,以及它们之间的关系定义清楚。如果说用例分析大部分情况下是程式化的(笔者正希望它是程式化的),那么你会发现,分析模型大部分工作也是程式化的。let's begin!

现在,我们有这样一些原料,用例场景提供了需求的输入,领域模型提供了初始的业务原材料,还有用例规约和补充规约提供了详尽的规则。正如笔者在之前的文章中提到的一句话:用例场景非常非常的重要,后续的工作就靠它了。这句话开始起作用,我们的第一步,就从用例场景开始。

做分析模型的建筑材料就是四个分析类,我们要用它们来搭建用例场景。actor分析类来自用例分析中的actor,实体类来自领域模型,边界类来自用例场景中actor-计算机交互,控制类来自业务规则(包括用例规约中的前置、后置条件、业务规则以及补充规约中的全局规则)。所使用的工具是时序图,目标是实现用例场景。我们先来做一个草图,对照着用例场景图,一步步来,得到这个结果:

  • 分析模型草图(图片比较大,由于Blog框架的问题,如果看不全,可以在图上右键->图片另存为,保存到本机再看)

我们来分析一下上面的草图。首先,这幅草图大家可以看到,所有的实体类没有做任何变动,直接照搬了业务实体(领域模型),所谓的控制类,只是机械的在每一个实体前加了一个控制器,边界类只用了一个。至于过程,更是和用例场景一模一样,只不过形式不同而已,改用了计算机术语。这个例子只有一个用例场景,如果有多个,每个场景画一次,重复用到的类直接拖过去,能用的方法直接用,还没有的就加上,总之忠实的实现这些用例场景就好了。整个过程很简单,很程式化,对吗?不用脑子都能做。尽管如此,我们仍然得到了一个分析模型的静态图,就象下面这样:

小提示:在做分析模型时,从时序图开始做,需要用到一个分析类时,就转到类图中创建这个类,再从左边的树形列表里把类拖到时序图上。从一个对象画一条表示交互消息的箭头到另一个对象后,右键点击线条,选择"new operation",再输入操作名,这时Rose会在线上标明这个操作名称的同时在对应的类上创建这个方法。这样,在绘制时序图的同时也生成了静态类图。最后再把类之间的交互用线连起来表示这个关联关系,静态图就完成了。

  • 分析模型静态图

再来一个小提示:rose对中文的支持不好,因此上面的图中类下面无法完整的显示用中文写的方法名,不过双击open sepcification对话框能正确显示。相信大家注意到我所有贴上来的图文字都是仿宋体而不是通常用的宋体,这是因为在字符集里,宋体并没有被限定为GB2312,所以当把rose里的图全选并拷贝到画图或WORD里时会文字会变成乱码。大家一定也遇到过这种情况吧?一个小技巧就是用仿宋字体,预先设定字体或者全选(crtl+A会吧),再菜单format->font,选仿宋,你可以看到在仿宋字体后面带了GB2312的字样^_^。这样就不用先屏拷再剪切,再拷贝到word里了。用word2003的朋友幸运了,没有这个问题。

不可否认,这个类图很粗糙,但是一个系统原型已经出来了。我们得到了可以完全实现需求的一些类,主要的类方法也有了。如果你想偷懒的话,直接把它转换成设计类图,把中文方法名改为英文,再把领域模型文档(不记得了回头查以前的文章)中提到的实体属性填入,就已经可以交付开发了。因为需求已经实现了,类有了,方法有了,类属性有了,别忘了在用例分析过程中已经用静态HTML做了系统原型,因此界面也有了。一切开发需要的东西都全了。比如我们用Strus+hibernate架构,一个实体类就是一个POJO,一个控制类就是一个action,至于界面,在静态HTML里填入java代码改成JSP呗。如果你是一个开发人员,把这个图给你,相信你也会觉得开发这个需求是很明确很简单的事吧?呵呵,成就真不小,可是仔细想想,我们甚至都没有动脑子啊!真是的,我们没有动脑子,居然就做了一个设计,恩!?可事实就是这样,谁说分析设计就一定要动脑子?不动脑子就不能做设计吗?就不能体现一个设计师的价值吗?设计的目的是什么?漂亮?好看?采用了很多新技术?体现了设计师的高明手段和渊博知识?NONONO!设计的目的是为了实现需求不是吗?如果需求就是这样简单,我们已经实现了,还能不动脑子,干嘛做那没事找抽型的,老板又不因此少付一分钱工资,能少干点活儿不好吗?很多设计师因为懂得几个设计模式,总要想方设法弄上去,把简单的问题弄复杂,好象只有这样才能体现他的价值。爱因斯坦说宇宙的法则是简洁的才是最美的,设计也是如此。设计师的真正价值,是用最简单,最好懂,最简洁的设计完成最复杂的要求,而不是相反!一项技术,正因为能够被大多数人掌握才能流行,否则就只能呆在实验室里,不是吗?

话扯远了,忍不住又抨击了一下过度设计,很不幸被抨击的对象也包括以前的自己^_^。好吧好吧,我知道尽管你会同意我的话,你还是会在心里嘀咕,如果设计就只有这一点东西,连脑子都不用动,那设计师也太好干了吧?分析模型如果就只有这一点内容,还要专门写个系列文章,这个作者也是个欺世盗名的吧?

呵呵,为了抚平你的失望,我告诉你。之所以我们没动脑子,是因为系统分析员们经过用例分析系列中的卓越工作,给我们打下了如此坚实的基础,才使得我们的工作简单到了不用动脑子的地步。你还得感谢UML用一套很好的方法,让你可以轻而易举的从需求转化到类设计。你已经站在巨人的肩膀上了,所以就一边儿偷着乐吧。

乐完了还得回到现实。在很多情况下,事情并没有这么简单。这个粗糙的分析模型虽然可以工作,但的确有很多可以优化的地方。不用担心你会因为做了一份简单而不用动脑子的工作而失去饭碗。因为下一篇,我们将会讨论怎么样,从哪些方面,以及如何依据补充规约,系统架构以及维护要求等来优化和调整这个模型。这下设计师有用武之地了,学习到的知识和积累的经验要发挥作用了,失落的自我价值也能体现了。为了证明设计师不是混吃喝的并且保住饭碗,敬请期待下篇,分析模型的优化和调整。

(3)--分析模型的调整和优化

草图代表了需求的实现,是一个细节的表露。接下来的优化的调整,就以此为基础。主要的输入:草图,系统架构,业务规则,补充用例规约,系统原型。主要的输出:调整后的分析模型,子系统,组件视图和部署视图(针对分布式应用而言)。

这一篇拖了很长时间,除了懒之外,另一个主要原因是一直找不到思路。想归纳一下自己的设计经验,找到一个相对容易学习的办法,结果总是不得要领。终于不得不承认设计工作是一项创造性的工作,是没有办法用什么固定的流程,普适的方法来完成的。除了知识和经验之外,个人的悟性恐怕也是影响设计好坏的原因之一。这篇文章写得很费劲,发现归纳出与具体需求无关的通用的一些方法真的很困难。相信对读者来说这篇恐怕是到目前为止最难懂的一篇了,因为太多的东西是只可意会不可言传的。如果让读者觉得困难了,只能说声抱歉,我已经尽力了。市面上所有有关设计的书目,无非是讲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的就是界面了,虽然用界面来讲这个例子不太合适,但思路是可以借鉴的。调整交互集中问题的方法有,重新规划职责或增加冗余,或增加中间调合层等等....这个结果可参看后面的分析模型图。

接下来,是根据上面的讲述产笔者提供的一些例子。这些例子只可意会不可言传的,很难说明,能否理解就看读者个人在OO设计上的经验了。

分析模型原图:

  • 针对业务规则:actor所有操作都应该被记录,分析模型所建立的一个解决方案。但绝不仅这一个方案。

    类图:

    交互图:

  • 针对业务规则:actor存取资源时应当被授权,分析模型所建立的一个解决方案,但绝不仅这一个方案。

    类图:

    交互图:

  • 调整耦合度,分析模型重新调整后的结构,但绝不仅这一个方案。

    类图:

    交互图:

  • 交互集中点调整,分析模型所建立的三个方案。由于笔者的案例简单,以前的例子中未出现交互集中点,因此用界面作为例子来讲。读者应当把这个例子中的界面设想为一个Control类或是一个Entity类会更加合理。不过思路是相通的。当然解决方案绝不仅止这三种
    方案一:重新规划职责

    方案二:增加冗余

    方案三:增加中间调合层

一个组件图的例子,网上交费业务的组件视图

再次提醒读者,这些例子只是针对某个问题的解决方案之一,不可避免的带着笔者的个人经验色彩。这些解决方案不是真理,没有高下,甚至没有对错。目的只是为了提供思路,而非模板和教条。熟悉设计模式的读者可以从设计模式中找到很多解决这类问题的模式。读者不应该拘泥于研究笔者这些解决方案,这些方案仅是为了配合说明调整分析模型关键点而提供的示例而已,重要的是理解那几个关键点。

这一篇当中,笔者讲述了一些调整分析模型的要点,并举了几个例子。笔者不得不说的是,写这篇文章真的花费不少力气。设计是一种创造性工作,随环境而变,随要求而变,随设计师而变...要总结出一套方法来实在是困难。笔者只能就自己的经验写一些要点。诚然,这些例子不足以解决所有问题。目前估计没人能做到"统一设计方法",真到了那一天,软件就是工业化生产了。笔者希望通过这几个要点和例子,能够激发读者的思路,举一反三,而不是照葫芦画瓢(我相信想画也画不出来)。今天这篇同时也揭示了分析模型到底要做些什么。读者也可以体会一下,调整分析模型与原来没有分析模型时调整设计类相比哪个更容易些,工作量更小些。

接下来,要讨论如何转化到设计类的问题了。但是设计类是实现类,它必然与软件框架,系统架构,实现语言息息相关。所以下一篇将讨论软件框架和系统架构在UML里的表现方形式,再下一篇才讨论分析类到设计类的转化问题。敬请期待。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织