
5.2.3 用例
1.用例的概念
用例(Use case)是参与者(角色)可以感受到的系统服务或功能单元。它定义了系统如何被参与者使用,描述了参与者为了使用系统所提供的某一完整功能而与系统之间发生的一段交互作用。用例最大的优点就是站在用户的角度上(从系统的外部)来描述系统的功能。它把系统当作一个黑箱子,并不关心系统内部是如何完成它所提供的功能,只表达整个系统对外部用户可见的行为。
UML中通常以一个椭圆图符来表示用例,用例名称书写在椭圆下方,如图5-9所示。
每个用例在其所属的包里都有唯一的名字,该名字是一个字符串,包括简单名和路径名。用例的路径名就是在用例名前面加上用例所属的包的名字,如图5-10所示为带路径名的用例。用例名可以包括任意数目的字母、数字和除冒号以外的大多数标点符号。用例的名字可以换行,但应易于理解,往往是一个能准确描述功能的动词短语或者动名词词组。

图5-9 用例

图5-10 带路径名的用例
用例和参与者之间的关系属于关联关系(Association),又称作通信关联(Communication Association)。关联关系是双向的一对一的关系,这种关系表明了哪个参与者与用例通信。
需要注意用例的一些特征。首先,用例必须由某一个参与者触发激活后才能执行,即每个用例至少应该涉及一个参与者。如果存在没有参与者的用例,就可以考虑将这个用例并入其他用例之中。
其次用例也是一个类,而不是某个具体的实例。用例所描述的是它代表的功能的各个方面,包含了用例执行期间可能发生的各种情况。例如,从ATM系统中取款这个用例,张三持银行卡去取钱,系统收到消息后将钱送出的过程就是一个实例。而李四持银行卡取钱,系统收到消息后因为钱已经取完而将银行卡退给李四也是一个实例。
注意,用例是一个完整的描述。一个用例在编程实现的时候往往会被分解成多个小用例(函数),这些小用例的执行会有先后之分,其中任何一个小用例的完成都不能代表整个用例的完成。只有当所有的小用例都完成,并最终产生了返回给参与者的结果,才能代表整个用例的完成。
2.识别用例
任何用例都不能在缺少参与者的情况下独立存在。同样,任何参与者也必须要有与之关联的用例。所以识别用例的最好方法就是从分析系统参与者开始,在这个过程中往往会发现新的参与者。当找到参与者之后,我们就可以根据参与者来确定系统的用例,主要是看各参与者如何使用系统,需要系统提供什么样的服务。可以通过以下问题来寻找用例:
- 参与者希望系统提供什么功能?
- 参与者是否会读取、创建、修改、删除、存储系统的某种信息?如果是的话,参与者又是如何完成这些操作的呢?
- 参与者是否会将外部的某些事件通知给系统?
- 系统中发生的事件是否通知参与者?
- 是否存在影响系统的外部事件?
除了通过与参与者有关的问题来发现用例,还可以通过一些与参与者无关的问题来发现用例,例如系统需要解决什么样的问题,系统的输入输出信息有哪些。
需要注意的是,用例图的主要目就是帮助人们了解系统的功能,便于开发人员与用户之间的沟通,所以确定用例的一个很重要的标准就是用例应当易于理解。对于同一个系统,不同的人对于参与者和用例可能会有不同的抽象,这就要求我们在多种方案中选出最好的一个。对于这个被选出的用例模型,不仅要做到易于理解,还要做到不同的人群对它的理解是一致的。
3.用例的粒度
用例的粒度指的是用例所包含的系统服务或功能单元的数量。用例的粒度越大,用例包含的功能就越多,反之则包含的功能就越少。
对同一个系统的描述,不同的人可能会产生不同的用例模型。如果用例数量过多,就会造成用例模型过大和引入设计的困难大大提高。如果用例数量过少,就会造成用例的粒度太大,又不便于进一步的充分分析。
如图5-11所示为学生管理系统中的学生信息维护用例,管理员需要添加、修改、删除学生信息等操作。还可以根据具体的操作把它抽象成3个用例,如图5-12所示,它展示的系统需求和单个用例是完全一样的。

图5-11 学生管理系统

图5-12 细化后的学生管理系统
当大致确定用例数量后,就可以轻松确定用例粒度的大小。对于比较简单的系统,因为系统的复杂度一般比较低,所以可以适当加大用例模型一级的复杂度,也就是可以将较复杂的用例分解成多个用例。对于比较复杂的系统,因为系统的复杂度已经很高,这就要求我们加强控制用例模型一级的复杂度,也就是将复杂度适当地移往用例内部,让一个用例包含较多的功能。
用例的粒度对于用例模型来说是很重要的,它不但决定了用例模型级的复杂度,而且也决定了每一个用例内部的复杂度。在确定用例粒度的时候,应该根据每个系统的具体情况,具体问题具体分析,在尽可能保证在整个用例模型容易理解的前提下决定用例粒度的大小和用例的数量。
4.用例规约(Use Case Specification)
用例图只是在总体上大致描述了一下系统所提供的各种服务,让我们对系统有一个总体的认识。但对于每一个用例,还需要有详细的描述信息,以便让别人对于整个系统有一个更加详细的了解,这些信息包含在用例规约之中。而用例模型指的也不仅仅是用例图,而是由用例图和每一个用例的详细描述—用例规约所组成的。每一个用例的用例规约都应该包含以下内容:
- 简要描述(Brief Description),对用例作用和目的的简要说明。
- 事件流(Flow of Event),包括基本流和备选流。基本流描述的是用例的基本流程,是指用例“正常”运行时的场景。备选流描述的是用例执行过程中可能发生的异常和偶尔发生的情况。基本流和备选流组合起来应该能够覆盖一个用例所有可能发生的场景。
- 用例场景(Use-Case Scenario),是指同一个用例在实际执行的时候会有很多不同的情况发生,也可以说用例场景就是用例的实例,用例场景包括成功场景和失败场景。在用例规约中,由基本流和备选流的组合来对场景进行描述。在描述用例的时候要注意覆盖所有的用例场景,否则就有可能遗漏某些需求。
- 特殊需求(Special Requirement),是指一个用例的非功能性需求和设计约束。特殊需求通常是非功能性需求,包括可靠性、性能、可用性和可扩展性等。例如法律或法规方面的需求、应用程序标准和所构建系统的质量属性等。
- 前置条件(Pre-Condition),执行用例之前系统必须所处的状态。例如,前置条件是要求用户有访问的权限或是要求某个用例必须已经执行完。
- 后置条件(Post-Condition),用例执行完毕后系统可能处于的状态。例如,要求在某个用例执行完后,必须执行另一个用例。
因为用例规约基本上是用文本方式来表述的,所以有些问题难以描述清楚。为了更加清晰地描述事件流,往往需要配以其他图形来描述,如加入序列图适合于描述基于时间顺序的消息传递和显示涉及类交互的一般形式,加入活动图有助于描述复杂的决策流程,加入状态转移图有助于描述与状态相关的系统行为。还可以在用例中粘贴用户界面或是其他图形,但是一定要注意表达得简单明了。