Susan Lilly 著,qian_x_j译
无论是你的系统边界是模糊的,还是你在用户接口中的用例被搞得混乱,你很快会发现你一开始建模时会很容易趋向于一些相当可预见的问题领域。不过你走运了,这也很容易避免。
用例是一项日益流行的技术,用来文档化系统和软件的需求。凭借它们简单的图形符号和接近自然语言的规格定义,用例对开发团队颇具吸引力——甚至那些对常规需求规范经验甚少的团队也是如此。然而,这个简单是有欺骗性的。虽然开发项目的团队使用用例,一开始没有遇到大麻烦,但是许多团队在较大的场合下应用用例时,会遭遇到相似的问题。
虽然从实践中学习可能是件好事,但是这在商业世界里却是昂贵的教育方法。你的项目团队不必要亲身经历这10个最常见的陷阱。我有四个建议,可以帮助避免许多常见的用例陷阱,并且其中有一个将帮助你发现并且移除陷阱。
|
[1]系统在哪儿?(系统边界)
要清楚系统边界。用例模型具有直接的符号。在它的最简单的形式中,它仅仅是一个标志性的盒子来描述系统边界,而角色(棒人形)画在盒子外面,用例(椭圆形标记)画在盒子里面。线和箭头连接着角色和用例。
但是我们称为系统的盒子是什么呢?我们是在谈论计算机系统么?是一个应用程序?是一个子系统?抑或一整个商业企业?用例可以合理地描述这些任何的系统边界,但是它们应该在一个时间里只注意一点。特定的角色和用例对某个系统边界是恰当的,却很可能对另一个不同的系统边界是不正确的。
让我们看看一个棒球订票系统的例子吧。一个亭售用户(售票亭买票的客户)使用计算机来订票,或者一个电话用户拨打电话号码800来进行这次票务生意,而电话员(一个售票业务的雇员)使用计算机系统来订票。谁是角色呢?在混合系统边界的用例图中,建模者试图在同一个用例图中同时体现业务用户和计算机用户。订票用例的文本规格定义也变得混乱:因为电话用户和业务的交互的集合是跟其它角色和计算机系统的交互的集合是不同的。
边界的不明确或者矛盾(陷阱1),可能是在我所观察过的许多第一次使用用例的项目中,最普遍的问题。当系统边界被搞乱了,你就很难知道要输入什么,要输出什么。你的工作可能会由于以下情况而终止:
。过多的用例(陷阱4)。
。角色和用例的关系混乱(陷阱5)。
。庞大的,却又混乱的用例规格定义,尝试描述多个范围层次(陷阱6和7)。
。用例不能终止,因为你的团队试图让“整个大洋燃烧起来”(尝试分析每件事情)。
这些问题可以通过明确范畴并标出系统边界来避免。比如,系统边界可以描述为计算机系统,亭售用户和电话员作为订票用例的角色。或者,系统边界可以描述为整个企业。电话用户作为角色,是一个售票业务的用户,但并非计算机系统的用户。两者都是合理的模型;如何选择它们之中的一个,取决于你正试图定义计算机系统需求,还是在业务过程建模或重构中使用到的用例。
一个相关的问题是,整个系统的边界丢失。这种问题经常发生在用例模型使用可视化建模工具(比如 Rational Rose,市面上面向对象建模的头号工具)上,它们不允许在用例图中放置边界盒。
如果你正在使用类似的工具,你仍应该努力使系统边界明确。即使它(边界)并不在图上,它也应该在你的脑子里。在图上放置角色和用例时,要假象那个盒子在那里。
[2]模板
为你的用例规格说明使用一个标准化的模版。虽然用例符号已经成为面向对象管理组织的统一建模语言(UML)中的标准,但用例的自然语言规格却没有。若想要在你的工程中成功的书写用例规格说明,为它们创建一个模版吧(当你真正需要使用它时就更适合了)。模版提供了用例和鼓励坚持项目标准的可靠性。
由于缺乏工业标准,许多相似的领域集里的用例规格模版被提议。用例名(Use Case Name),就是出现在用例图中椭圆形下面(或者里面)的名字。角色(Actor)就是启动用例的主要角色。从对于主要角色或角色们的观察来看,目标(Goal)就是用例目标的一个简洁描述。当触发器(Trigger)作为具体事件导致用例启动,在用例应用中的环境(Context)就是背景“故事”了。正常流(Normal Flow)包括几个步骤,它们描述在角色和系统之间通常发生的最典型的交互序列。变体流(Alternative Flows)是正常(可成功的)流的变体路线,每一个都描述了它的触发条件(是什么导致从正常流跳入),过程和结果。异常流(Exception Flows)是错误的路线,每一个都描述了它的触发条件(是什么导致从正常流跳入),过程和结果。还有一个可选的问题领域是,指出所有的假设前提,就是用例是基于什么的。至少,用例规格应该回答这些基本问题:
。谁?(角色)
。为什么?(目标和/或环境)
。何时?(触发事件)
。什么?(正常流)
。还有什么?(变体流和/或异常流)
另外,对于在每个用例中的定义域,定义一个标准也很有帮助,那些信息是要为一个小组,或者一个相关用例的“包”而说明的。这些信息应该包括角色名和其它重要术语的术语表(防止陷阱3),还包括交叉用例的业务规则,一个“故事”(或者动态图(active diagram))显示用例在时间过程中的关系,还有其它等等。
在我的公司,我开发了微软word形式的模版来定义用例包,具有自定义风格的主要元素及其帮助说明。你如果想要把基于word的文档自动的导入需求管理工具,自定义风格(word)是非常有用的。内建的帮助说明旨在方便那些用例书写者,因为他们不愿意在写文档时回过头去察看项目标准或者过程要点。它提供了,符合用例命名惯例的提示语(比如,以“action”动词开头的短语),规格定义规则(比如,使用主动形式的主谓句;避免逻辑分支),还有实用的提示。
设计一个工程的用例模型,你不必从打草稿开始;你可以从网上获取例子并且裁剪它们。Alistair Cockburn 有一个网址(http://members.aol.com/acockburn/papers/OnUseCases.htm),那儿提供了很多有用的用例支援,包含了用例模版,有HTML形式,微软的Word形式和文本形式。
[3]观察点(目标,而非琐碎的东西)
注意目标。在外观察以上来看,基于用例的需求对于传统的系统需求(比如,“Shalls”)的最大差异是棒人形了。这代表的意义并非完全是它的样子所要描述的,它旨在强调规格化系统要基于已定角色(人类用户和其他扩展实体)的观点。在面向用例的世界观里,系统的存在是因为,角色有一些让它们使用而满足的目标。我们书写用例,是要指出角色和系统之间交互的本质,结果就是角色目的满足。(Ivar Jacobson,用例之父,使用术语“值的结果(results of value)”来暗示在用例中考虑角色的真正需要或者目标的重要性)
把这一点牢记在心,我们应该能够扫视用例模型,并且列举那些在用户使用系统中想要做的事。这些并非是琐碎的用户的交互,而是真正的目标。选择那些反映附带的行为,而不是真正的角色目标,会导致过多的用例(陷阱4),并且导致用户的问题表述同基于用例的需求规格的脱离。
所以,要选择好的用例,就要注意那些反映角色的真正的目标。然后,从角色角度命名用例,而不是系统角度。
比如,“处理订票”和“显示比赛安排”是系统要做的,所以不是好用例名。“订票”和“察看比赛安排”是系统用户的目的,这才是好的用例名。
另外一个用户目标获取的迷失具有讽刺意味,某些建模者期望于用例“面向对象”。这种情形的症状是,存在臃肿的“CRUD”用例,那包括了所有的对于业务对象可能的行为。(“CRUD”是对象创建(Create),读(Read),更新(Update),删除(Delete)能力的首字母。)这些用例经常包含的名字有“维护”(“maintain”),“管理”(“manage”)或者“处理”(“process”)。
CRUD用例有什么错呢?第一,也是首要的,它们经常不是直接的相关于角色目标。它们经常描述许多角色目标的联合体,来自于多个业务环境和过程,由于许多目标共享一个对象而被组合在一起。CRUD用例还有什么错呢?它的规格定义是典型的过于冗长(陷阱6),它经常会关联过多不同的角色(陷阱5)并且这些角色对于整个用例的“功能定义”是典型的令人怀疑(陷阱8;还有其它)。简言之,当我们运用面向对象方法分析时,很多时候我们要注意于对象,但是选择用例本身作为对象并非恰当。
不要把用例规格定义同用户界面设计混淆起来。有一个流行的观点,就是:因为用例有关于角色和系统之间交互,所以把用户界面放到用例规格定义中去是个好主意。粗粗一看,屏幕快照好像很有用,因为它们在规格定义中描述了角色/系统交互。
然而,坏处远大于好处。假如我想要使自己掉入了陷阱10(“用例从来不会结束”),我可以把屏幕快照(界面)放入我的用例规格定义中。这儿有个我观察过的项目,曾经这样做:用例,试图定义系统操作需求,并且完结于包含系统的用户界面设计。一旦需求需要被核准并且被定为基准;这些设计元素,很自然的,还是要被更改。所以用户还是不能停止制作需求文档,因为它包含了已经完成了的用户界面设计,错误的和/或矛盾的。
即便是用例已经被核准,它会在短期内是成功的(短期维持)。然而用户界面设计很可能随着时间而需要更改。我们不想把系统需求依赖于设计。这种依赖应该用其它方式解决——用户界面设计必须满足用例需求。
在把界面放入用例的工程中,我还见到过其它问题。为了尝试反映在用例与屏幕之间一对一的通信,他们选择了反映大量用户界面而非用户目标的用例。(典型的,用户目标比用户屏幕“更加”值得交互。)他们试图通过使用内建用例关系(“使用”或“扩展”),把大量的界面用例揉合到完全的用户目标中去。这样就导致了在例用模型中,形成了关系蜘蛛网,过多的描述了用户界面的切换,而忽略了用户目标和功能定义。用户界面切换图是一个有用的系统文档;然而,它是属于用户接口(设计)文档,而非用例(需求)文档。
一个更好的方法是,让用户接口细节和用例交互松散的耦合。一点点的耦合是合适的;包括“低分辨度”的用户界面图片,用于理解用例。但是不要过分的把功能定义同用户接口(UI)机制结合起来。用户接口更容易发生更改。在规格定义中,注重于建立角色要做什么(比如,“选择一个游戏”,“提交一个请求”),而非怎样交互(比如,“双击提交按钮”)。
另外,不易被发现的问题是,试图在用例和用户接口之间通信可能导致不正确的功能定义(陷阱8)。
比如,若我们声称很多用户能够使用赛程安排界面来察看赛程,但是通过适当的验证,一个用户(比如,赛程管理员)还可以使用那个界面更新赛程安排。建模者决定把察看和更新功能组合到同一个处理赛程的用例,基于公共的赛程用户界面。
这个用例跟只能察看界面的角色关联,当然也跟能够更新的角色关联(也许是一个变体流)。这个结果是不正确的功能定义。只要看看这个模型,它呈现的是,亭售用户可以察看并且更新赛程,如同赛程管理员能做的一般。分离各个用例,要基于实际的用户目标和功能定义,而非组合基于用户接口,这样才会得出正确的模型。
[4]经常检查(复查)
复查你的用例图和规格定义。假如你不能防止所有的陷阱,你至少能够尽可能早的截取问题并且修正它们。这个主意并不是新的并且迷人的。由这种经典的方法,进行代码走读已经经历了25年了,而且相当于这种复查过程的应用,在成熟度模型等级3(Capability Maturity Model’s(CMM) Level Three)中是一个关键的过程。
怎样把它应用到用例呢?在我们公司的项目中,应用多层通过的复查过程,导致了好的结果。首先,用例图的复查是,通过察看用例规格定义的简略的存根(仅仅是用例名及其目标或者简单描述)。
在对于用例规格定义进行细化以前,对于草图,这种复查进行的越早越好。对于草图复查以后,由于图是正确的,用例规格定义的细化便可顺利完成了。然后,执行最终的用例图及其规格定义的复查。
我们发现两阶段复查过程要优于一次“大的”复查。在书写规格定义细节上投资许多时间以前,用例图中大量的组织性问题被揪了出来。当用例图需要重新组织时,这种方法缩减了必须做的重复工作的总量。
使用例复查有效的其它窍门:
。使用复查校验表来帮助发现常见的用例问题。
。描绘原素材来“讲一个故事”。有时候,描绘图形素材,要比平实的文字要吸引人。我们建议列出一个“广度优先”的概览,然后是若干小组深入的相关的用例。比如,描述者可以概述用例图的一部分,它是相关于一个角色或者一个特定的业务过程;然后通过细节遍历这些规格定义,然后回到用例图,考虑用例集合的另外相关的部分。但是永远不要把按字母排序作为描述的策略!(是的,我真的看到过有人这样做)
。在最初的模型复查时,考虑使用非常规的复查技术。我喜欢使用“黄色粘纸墙上复查(Yellow Sticky Wall Review)”的方法(名称来源于几年以前在IBM对象技术大学中的经历),对非常规的图形模式进行复查。用例图被打印出来,贴在进行复查处的墙上。复查者在黄色粘纸上写上他们的注解,把纸贴在用例图上模型附近合适的部位。
常见的错误
我所描述过的陷阱并非对用例的控告;应该说,对于那些从来没用过它们的初用者来说,它们是典型的困难;并且多数应用用例的开发团队确实存在新手。
此外,用户,共同参与需求分析的最终使用者和领域专家们,一般没有使用用例经历。由于建模符号的简单,并且是自然语言的规格定义,所以很容易上手。
然而,简单的形式不应该掩盖一个真相,就是需求分析和定义并不是次要的工作。很多团队在初次尝试用例时遭遇了相似的陷阱。
这些建议能够帮助你的团队避免这些问题。
链接:http://www.umlchina.com/xprogrammer/issue/5/avoidusecasepitfalls.htm