5.2 功能需求的表达
5.2.1 基于用例的功能需求获取
需求是一个普遍使用的词汇,但又是一个颇为复杂的概念。需求具有不同的抽象层次。有些需求非常宏观笼统,例如对教室预订系统的一个需求是“系统使用要很方便”,有些需求则非常细节具体,例如教室预订的时候先要进行登录。显然,这种对需求的不同理解不利于需求的获取。
用例(use case)提供了以一致的方式来获取和表达功能需求的手段。不同于一般的直接以文字来进行需求表达的方式,用例是从用户的角度,把整个系统看作黑盒子,通过描述在完成功能过程中用户与系统的交互来刻画需求的手段。
用例是一系列动作和事件的列表,它定义了单个外部参与者与系统之间的交互,通过这种交互,系统提供了可见的价值。
为了理解用例,我们需要区别以下概念:
(1)参与者(actor):是位于系统外部、与系统具有交互的事物,可以是人、计算机系统或组织,例如教室管理员。需要注意的是参与者不是某一个具体的人、系统或组织的抽象,而是一类人、系统或者组织。在UML中,参与者的符号如图5-1所示。
图5-1 参与者的UML符号
(2)场景(scenario):某一特定的参与者和系统之间的一次交互,也称为用例实例。场景是使用系统的一个特定情节或用例的一条执行路径。在获取需求时,我们通过列举若干重要的场景来发现用例。通俗地理解,场景就是讲一个有主人公参与的一段故事,这个故事会分为若干步骤来描述。例如可以针对教室预订系统,讲述小王如何在网上预订教室的一个故事。
●场景名称:教室预订
●参与者实例:教室申请人员小王,教室管理员小张
●事件流:
1.小王根据活动需要打算使用教室预订系统预订一间教室。他在已接入互联网的终端上激活了教室预订系统中的教室预订功能。
2.小王填写一份包含申请人信息、使用时间、使用目的、活动人数、希望安排的教室规格和位置的表单并提交。然后小王等待系统的教室预订答复信息。
3.教室管理员收到系统提醒后,审查小王的申请表。小张根据目前的教室预订情况以及小王的申请信息安排相应的教室并确认。
4.小王通过预留在系统中的电子邮件收到了来自教室预订系统的确认信息,教室预订成功。
图5-2 用例的UML符号
(3)用例(use case):对相关的各种场景(包括成功的、失败的场景)的概括,用来描述参与者如何使用系统实现其目标。场景是有具体的主人公参与的故事,用例则要把这些故事上升为一个一般性的过程,在这个过程中主人公被角色所替代,描述中的一些具体细节被通用的信息所替代。在UML中,用例的符号如图5-2所示。
5.2.2 用例的编写方法
用例建模并非仅仅是画一个用例的UML符号,而主要是通过文字对用例进行表述。用例的编写可以有不同的详略程度:在早期,可以是简略的写一段话,也可以用几段话描述,而在详细描述的情形下,需要按照规定的格式,甚至用几页纸来加以定义。
用例的编写格式如下:
1)用例名字
在给用例起名字时,一般采用一个动宾词组,如果你发现很难用一个动宾词组来刻画,那么很可能是用例选择不恰当造成的。
2)范围
说明该用例是系统用例还是业务用例。系统用例是软件系统本身的用例。业务用例是业务功能的描述,通常用于对业务分析。
3)级别
说明该用例是用户目标级别还是子功能级别。用户目标级别代表了一个参与者会发起的完整用例。子功能级别代表了可能在几个用例中可以重用的子功能用例,进一步的说明参考5.2.3节。
4)主要参与者
定义这个用例中的参与者。用例必须有一个发起者(initiating actor),同时可能有若干个参与这个用例的参与者(supporting actor)。注意参与者并不一定是人。
5)涉众及其关注点
定义在此用例中各个人员关注的因素。
6)前置条件
定义该用例发生的前提条件。
7)后置条件
定义该用例完成后,系统中发生的改变以及对参与者的影响。
8)主流程
以事件流的方式定义参与者与系统进行的交互过程。在描述时必须能够清晰刻画每一个事件的参与者以及产生的结果。主流程指的是正常的、一般的情形。那些意外的情形将放在扩展中。
9)扩展流程
以事件流的方式描述各种意外或者特别的情形。必须说明是从主成功场景的第几步跳到扩展中描述的事件流片段进行执行,然后再返回主流程。
10)特殊需求
描述与该用例相关的非功能需求,质量属性或约束。
11)发生频率
描述该用例发生的频繁程度。
以下为教室预订系统的一个用例的例子。
●用例名称:Submit Application
●范围:系统用例
●级别:用户目标
●主要参与者:教室申请人员
●涉众及其关注点:
教室申请人员:希望能够方便、快捷地提出申请。
●前置条件:教师或学生具有合法的身份。
●后置条件:产生教室申请单,等待审批。
●主流程:
1.教室申请人员输入身份信息。
2.系统判别身份信息是否有效。
3.教室申请人员根据活动需要向系统提交教室使用申请,包括申请人、使用时间、使用目的、活动人数、希望安排的教室规格和位置。
4.系统将信息发送到教务系统。
5.教务系统判断符合条件的教室是否存在。
6.如果有符合条件的教室,系统生成申请单,等待管理员批准,告诉申请人员申请提交成功。
7.如果没有符合条件的教室,告诉申请人员申请无效。
●扩展流程:
1.在第3步提交申请过程中,网络连接出现问题。
2.告诉用户网络出现异常,提示用户稍后尝试,回到第3步。
●特殊需求:无
●发生频率:可能随时发生,短时间内可能有大量申请。
5.2.3 用例模型与用例图
将参与者、多个用例画在一张图上来完整地表达系统的功能需求,就形成了用例图,用例图加上用例描述就构成了用例模型。
参与者与用例之间的关系是一种通信关系,用带箭头的直线表示,箭头表示的是访问的方向(见图5-3)。发起参与者的箭头指向用例,而支持参与者与用例之间,有可能箭头指向用例,也有可能指向支持参与者。在建模过程中,箭头可以暂时不画出来。
图5-3 参与者与用例的关系
用例之间也存在一定的关系,它们可以是包含关系、扩展关系和泛化关系。
1)包含关系
包含关系描述的是一个用例需要某种功能,而该功能被另外一个用例定义,那么在用例的执行过程中,就可以调用已经定义好的用例。包含关系有以下两种典型用法:
(1)如果两个以上用例有一致的功能,则可以将这个功能分解到另一个用例中,其他用例可以和这个用例建立包含关系。
(2)一个用例的功能太多时,可以使用包含关系建立若干个更小的用例。
包含关系用虚线箭头上面加上符号≪include≫表示,箭头的方向从基用例指向被包含的用例,如图5-4所示。
图5-4 用例包含关系
图5-4展示了教室预订系统中一个具有包含关系的例子。在申请者申请预订教室的过程中,通过用例Check Availability检查申请教室的可用性。同样管理员在处理申请时,也可以通过用例Check Availability检查教室可用性。
2)扩展关系
扩展关系表示用一个用例(可选)扩展了另一个用例(基本用例)的功能。对扩展用例的限制规则:将一些常规的动作放在一个基本用例中,将可选的或只在特定条件下才执行的动作放在它的扩展用例中,用符号≪extend≫表示。
扩展关系的箭头方向是从扩展用例指向被扩展的用例。
图5-5展示了教室预订系统中一个具有扩展关系的例子。用户在提交教室预订申请的时候,可能遇到与教室预订系统连接关闭的情况,比如浏览器崩溃,或者申请人误关闭网页。当连接关闭时,Close Connection用例处理提交申请中发生的网络异常。
图5-5 用例扩展关系
3)泛化关系
泛化关系(generalization)是一种继承关系,子用例将继承基用例的所有行为、关系和通信关系,也就是说在任何使用基用例的地方都可以用子用例来代替。
泛化关系在用例图中使用空心的箭头表示,箭头方向从子用例指向基用例。
图5-6是教室管理系统中一个具有泛化关系的例子。提交申请用例(Submit Application)是一个笼统的提交申请用例,描述了提交申请的过程。提交一个新的申请(Submit New Application)、提交一个修改预订方案的申请(Submit Changed Application)是提交申请的特殊情况。
图5-6 用例泛化关系
通过将用例采用以上关系进行进一步合理组织后,就形成了用例图。
以教室预订系统为例,系统的用例图如图5-7所示。
5.2.4 用例建模流程与注意点
1)用例建模流程
用例建模可以遵照以下流程开展:
(1)识别系统边界:通过确定系统边界后,就规定了系统的范围,也能确定什么是系统内的,什么是系统外的。
图5-7 教室预订系统用例图
(2)识别参与者:对位于系统外,并且与系统交互的实体进行一一识别。
(3)对每一个参与者,识别其发起的用例:对于每一个参与者,识别其发起的用例。可以通过描述其涉及的典型场景,再在场景的基础上归纳出用例来。
(4)对用例进行修改:将不同用例中公共的部分抽取出来作为子用例,其他用例包含它;如果一些用例大部分都相同,只有小部分不同,那么抽取一个公共用例,其他用例继承它;将一个用例中较为复杂的特殊流程部分抽取出来作为扩展用例。
在以上步骤的基础上,可以构造完整的用例图。
2)用例建模注意点
用例建模是获取需求的有效方式,但是它本身并非面向对象方法学的一部分。在用其他方法学进行软件开发,包括传统的结构化方法进行软件开发时,依旧可以采用用例建模来获取需求。
需要强调的是用例建模主要是写文档,而不仅仅是画用例图。许多初学UML的人认为利用用例进行需求获取仅仅就是画出用例图,这显然是对用例建模的一种误解。
在用例建模时,经常面对用例的颗粒度问题。如果用例中包含的内容过多,换言之,用例的颗粒度很大的话,很可能系统中只有一个用例或者非常少的用例,这将给系统的分析、设计和实现带来困难。但是如果将很少的步骤都建模成用例,系统中可能包含了过多的用例。如何判断用例的颗粒度是合适的?首先从定义上说,用例包含了一般的场景和特殊的场景,所以用例是场景的集合。此外,每个用例必须能够给用户带来可见的价值。因此,一个用例应该对应一个基本业务过程单元。当然,那些被包含的用例或者扩展用例并不对应单独的业务过程单元,而是和其依附的用例构成完整的业务过程单元。
用例反映的是系统“做什么”,而不是“怎么做”。在用例中,表达的是用户希望如何通过该系统达成自己的目标。实现一个目标有多种方式,而且随着技术的进步,还可能出现新的实现方式。例如教室预订系统中,首先要进行用户身份识别,目前主流的方法固然是采用用户名、密码的方式,但是随着技术的进步,有可能采用指纹识别、声音识别等形式。所以在用例中说明是身份识别,而并不写清楚具体的密码输入手段,恰恰是一种刻画“做什么”的方式,而“怎么做”可以留待设计时再来确定。
在用例建模时,注意不要表示成功能分解。许多初学者可能对功能分解比较熟悉,因此,在用例建模时,他会用一个用例去包含许多子用例,子用例再包含子子用例。这种做法是不可取的。