程序员眼中的UML(2)
--克服用例图的恐惧
在实际工作中,大部分程序员很少接触到需求分析,即使有需求分析,也是草草了事,没有用正规的方式来表达,所以一般程序员使用用例图的机会是不多的。但是却又常常在各种媒体上看见用例图,于是对一种常常出现,自己又不太熟悉的技术,会产生恐惧。如果说对MDA或者CORBA这样的技术产生恐惧还是值得的话,对用例图产生恐惧是非常不值的。因为MDA和CORBA这样的技术也许要花上半年的时间才能够初步了解,而克服用例图恐惧症,则只要不到一天的时间。
用例图初感
UML是一组图示符号的标准。所谓图示符号,就是一组定义好的图示,它们可以表达定义好的各种意思。用UML进行软件建模,就是用规定好的符号画图,这些图表达了开发人员脑中的软件系统。用UML进行软件建模,其难度并不比我们小时候上的美术课更难。在美术课上,一个圆形加上四根线条表示太阳,一个三角形加上一个矩形表示房子;同理,在UML的用例图中,一个椭圆表示用例,一个小人表示参与者。我并不认为它们之间有质的区别,想到我对这种小学生画图课恐惧了几年,不由得感到羞愧。
用例图是UML的九个图中较为重要和常用的一种图。常常用于软件开发的需求分析阶段,也能用于软件的系统测试阶段。简单的来说,用例图是描述系统的外部视图。
在开始设计一个软件系统时(更广义的情况下,可以用来设计任何系统),需要一种手段来发现系统的功能,用例图虽然是图示,但是这些图示隐含了一种启发系统功能的手段。其实所有的UML图都只包含图示和标准,并不包含方法,但是它们往往隐含了某种方法。UML和软件开发方法的关系,很类似于汉字和语文的关系。
用例图包含了三种基本的概念:用例、角色和系统。它们可以组合起来表达系统的外部视图。而且这种表达方式是如此直观和简单。
第一张用例图
画用例图是一件很简单的事情,而且感觉还很舒适,因为用例图简洁、直观。虽然用例图不能像HelloWorld一样运行,也不能生成代码,不过画一张清晰的用例图还是很有成就感的。
我使用的工具是Eclipse+EclipseUML插件,功能不如Rose,但是是开源而且免费的(EclipseUML有free版也有企业版),而且效果也不错。第一张用例图如下:
可以看出图中有一个系统(保险商务系统),两个角色(客户和保险销售员)以及三个用例(签订保险单、销售统计资料、客户数据资料),另外还有四个连接线以及一个注释。如果在纸上或者合适的工具中,画这样一张用例大概只需要五分钟吧。不过仅仅画出来是没有意义的,需要弄清楚其背后真正的含义才行。
理解用例图
可以这样简单的理解用例图中的一些概念,系统(System)指的是软件系统,它可以包含一些用例,并界定系统的边界,边界之内的属于系统的功能和行为,边界之外的则不是系统所关心的内容。系统规定了一个具有某些功能的黑盒子,在系统之外看到的仅仅是这个系统的功能,而不能看到系统的内部细节。这一点也是用例图经常被用来做系统测试的原因。当然这些测试一般是黑盒测试。
角色(Actor)是与系统中的用例交互的一些实体,在实际情况中,角色可以是人,也可以是其他系统或者硬件设备。在画用例图的过程中,角色往往是第一个被确定的,因为系统或者用例在开始时是模糊的,但是参与系统的角色是最容易明晰的。有了角色之后,根据角色与系统的交互,以及角色要求的功能,可以进一步确定系统和用例。
用例(Use case)指的是系统的功能,它是系统某个功能的所有执行动作的集合。在UML图示中它是一个椭圆,但是具体分析用例的时候需要给出这个用例的所有执行动作的步骤。例如上图中的“签订保险单”用例,就可以分为几个步骤:第一,客户发出保险单请求;第二,系统给出保险单样式表;第三,用户填写保险单样式表;第四,系统检查用户提交的保险单格式是否规范;第五,如果不规范则返回第二步,如果规范则给保险单销售员发出消息;第六,保险单销售员填写保险单;第七,保险单销售员将填写好的保险单加入数据库,并将客户资料输入客户数据库。当然,以上步骤仅仅是我想象的,我还从来没有见过什么“保险单”,这次过了一把瘾。
连接(Assocation)是角色与用例的连接,表达此角色可以初始化此用例。
注释(Note)可以添加到任何地方,对用例图的不同部分加以说明。
泛化、包含和扩展
泛化(Generalization)在面向对象的技术中无处不在,它的另一个名字也许更为著名,就是“继承”。下图给出了一个使用泛化的用例图:
由此可知,在用例图中,角色和用例都能够泛化。角色的泛化/继承很容易理解,因为角色本来就是类(Class),它是一种版型(stereotype)为Actor的类,所以角色的继承直观而自然。但是用例的继承实际上分为两种情况,并不是简单的使用泛化,而是使用扩展(extended)和包含(include)两种泛化的特例。
扩展用于子用例的动作步骤基本上和父用例的动作步骤相同,只是增加了另外的一些步骤的情况下。包含用于子用例包含了所有父用例的动作,它将父用例作为了自己的一个大步骤,子用例常常包含一个以上的父用例。如下图:
小结
关于用例图基本上也就是上面提到的这些内容了。当然,用例图还常常和类图、活动图联合使用,不过那些知识还是等其他知识完备了以后再说比较好。
我总结的画用例图的步骤如下:
l 确定系统,拟出系统的名称,这个不难,例如电信计费系统;
l 找出所有与系统打交道的角色,角色要进行一些精简和整合;
l 站在角色的立场想象系统应该提供的功能,将这些功能画成系统中的用例;
l 对于每个用例给出详细的动作步骤;
l 找出用例图中角色、用例之间可能有的继承、扩展或者是包含关系;
以我现在的理解能力,认为用例图到此为止了。当然,我还可以想象,用例图会用来启发类图的构建,例如用例图中的某些部分(角色或者用例)会变成类图中的类或者接口。另外,可以想象用例图还可能会影响活动图中的流程。
后记
让我恐惧了好久的用例图在今天便土崩瓦解了,心中却彷佛有一点茫然,因为我记得自己无数次的对自己说“好忙啊,这个技术肯定要花很多时间,还是以后再学吧”类似的话。当我用C的时候对C++这样说过,然后是Java、CORBA、JSP、XML、MDA、XMI、UML……
写blog不仅仅是一种爱好,对我来说,更是一种最好的学习方法,当我读小学的时候,班主任常常对我说“好记性不如烂笔头”,我当时的理解是把事情用笔记下来比用脑袋背下来更加持久(脑袋是内存?纸笔是数据库?)。但是,现在我理解到,要学习某个东西,最好的方法是用自己的语言把它表达出来,如果你能让别人理解这个技术,你自己当然已经精通了。在我写blog的过程中,常常会出现写到一半的时候猛然领悟的情况,这是因为当你在选择最佳的表达方式的时候,将你自己头脑中一团乱麻的线索都理清了的结果。
有朋友留言说,自己计算机本科毕业,在计算机领域学习了八年,竟然看不懂我的blog。这个我认为很正常,读本科的时候,所有同学都可以在一起讨论问题、实习;等到上研的时候,同学对我说他的课题,我只能模糊的听个大概,因为专业方向已经分开了;现在读博了,同学要拉着我说他的课题,我只能明白是哪个领域里面的问题,往往对这个问题的描述都听不懂了。这是因为研究方向已经非常精细的原因。我自问我的方向已经很大众化了,如果研究图像压缩算法的话,那满篇都是数学公式了;如果研究微电子技术,那么满篇都是集成电路图了。
还有一点,如果我感兴趣的技术在网上已经能够找到很好的资源,那么我不会动笔去写blog了,我会把资料下载到硬盘就好了。例如XML技术,我的blog中只有两篇,第一篇是“XML的本质讨论”,总结出了别人看不到的XML本质,提出了一些新观点;第二篇是“XSL:转换从哪里开始”。其实XML技术浅显易懂,我也有很多感想和心得,不过其中只有XSL在网上不能找到很好的总结资料,因此我写了那一篇。这也是我坚持原创和创新的一点反映吧。对于那种狂贴别人资料的blog,我是不以为然的。我只喜欢原创或者第一手翻译资料的blog。