
3.1 用例图的构成
用例图是一种将用例和软件工具相结合的图形表示方式,它由参与者发起,主要显示了一组用例、参与者以及它们之间的关系。
3.1.1 什么是用例图
用例图是由软件需求分析到最终实现的第一步,它从用户角度来描述系统功能,描述系统的参与者与系统用例之间的关系。
用例图通常在进行需求分析时使用,由开发人员与用户经过多次商讨而共同完成,这些图以每一个参与系统开发的人员都可以理解的方式列举系统的业务需求。
用例图使用系统与一个或多个参与者之间的一系列消息来描述系统中的交互,它将系统功能划分为对参与者(系统的理想用户)有用的需求,其交互部分被称作用例。除此之外,用例图仅仅是从外部观察系统功能,也就是从参与者使用系统的角度描述系统中的信息,并不描述这些功能在系统内部的实现过程。
用例图不仅包含系统、参与者和用例3个元素,而且还包含表示这些元素之间存在的泛化关系、关联关系和依赖关系等各种关系。
用例图是描述参与者与系统的关系,因此用例图整体上分为3部分:参与者、系统和关系,通过关系将参与者与系统联系起来。下图描述了一个学生成绩管理系统的用例图,它是一个实际系统简化后的示例。

人形表示参与者;矩形为系统;椭圆形是用例;线条为关系,连接用例和参与者。在下面的小节中将对上述元素作详细介绍。
3.1.2 系统
系统是用例图的一个重要组成部分,用于执行特定功能。它不单指一个软件系统,而是为用户执行某类功能的一个或多个软件构件。如图书馆管理系统、学生选课系统、信息发布系统等都属于系统。
系统的边界用来说明用例图应用的范围。例如,系统拥有一定应用范围,例如一台自动售货机,提供售货、供货、提取销售款等功能,这些功能在自动售货机内的区域起作用,自动售货机外的情况将不考虑。
准确定义系统的边界并不总是很容易的,因为有些情况下,严格地划分哪些任务是由系统完成,而哪些是由人工或其他系统完成是很困难的。另外,系统最初的规模应有多大也应该考虑。一般的做法是,先识别出系统的基本功能,然后以此为基础定义一个稳定的、精确定义的系统架构,以后再不断地扩充系统功能,逐步完善系统。这样做可以避免由于系统太大,需求分析不易明确,从而导致浪费大量的开发时间。
系统在用例图中用一个长方框表示,系统的名称被写在方框上面或方框内。方框内包含了该系统中用符号标识的用例,如下图所示。

3.1.3 参与者
参与者是系统外的一个实体,它代表了与系统交互的用户、设备或另一个系统。
参与者是系统服务的对象,通过向系统输入信息或系统为参与者提供信息来进行交互,以实现系统功能。在确定系统的用例时,首要问题就是识别参与者。
1.参与者的概念
参与者用于表示使用系统的对象。参与者可以是一个人、一个计算机系统、另一个子系统或另外一种对象。例如,计算机网络系统的参与者可以包括操作员、系统管理员、数据库管理员和普通用户,也可以有非人类参与者,如网络打印机。参与者的特征是其作为外部用户与系统发生交互。在系统的实际运作中,一个实际用户可能对应系统的多个参与者。同样,不同的多个用户也可以只对应于一个参与者,从而代表同一个参与者的不同实例。
每个参与者定义了一个角色集合,当系统用户与系统相互作用时会采用它们。参与者的一个集合完整描述了外部用户与系统通信的所有途径。当系统被实现时,参与者也被物理对象实现。物理对象如果可以满足多个参与者的角色,那么它就可以实现多个参与者。例如,一个人可以既是商店售货员又是顾客。这些参与者不是本质上相关的,但是它们可以由一个人来实现。当系统的设计被实施时,系统内的多个参与者被设计成类实现。
在用例图中,参与者由固定的图形表示,并在参与者下面列出参与者的角色名。当为用例图中参与者命名时,给作为系统用户的参与者提供一个最能描述其功能的合适名称是非常重要的。当为参与者命名时要避免为代表人的参与者起一个实际的人名,而应该以其使用系统时的角色为参与者命名。例如下图表示的参与者,老师表示所有以老师身份使用系统的人,而并不单指某个人。

参与者与系统的交互作用量化为用例,用例是设计系统和它的参与者连接的功能块,用来完成对参与者有意义的事情。一个用例可以被一个或多个参与者使用,同样,一个参与者也可以与一个或多个用例交互。最终,参与者由用例和参与者在不同用例中所担任的角色决定。没有参加任何用例的参与者是无意义的。
用例模型刻画了一个实体(如系统、子系统或类)与外部实体相互作用时产生的行为的特征。外部实体是实体的参与者。对于一个系统,参与者既可以由人类用户实现,也可以由其他系统实现。对于一个子系统或类,外部元素可以是整个系统的参与者,或者参与者可以是系统内的其他元素,如其他子系统或类。
在建模初期,参与者和用例交互,但是随着项目的进展,用例被类和组件实现,这时参与者也发生了变化。参与者不再是用户扮演的角色,而变成了用户接口。例如,系统分析阶段的用例图中,图书管理员与借出书目用例交互,以借出某本图书。在设计阶段,该参与者就变成了两个元素,即图书管理员这个角色和图书管理员所使用的接口,用例在这时就变成了许多对象,负责处理与用户接口以及系统的其他部分交互。
2.识别参与者
一个系统在建模之前虽然能确定一些用户和参与者,但并不能全面地不遗漏地将参与者找出,这将导致建模不完善、开发不完善,开发过程中的修改又将导致开发效率降低,漏洞产生。
全面识别参与者才能使建模很好地进行下去。为了能找出所有参与者,可以借助以下几个问题。
□ 系统的主要客户是谁?
□ 谁需要借助系统完成日常工作?
□ 谁来安装、维护和管理系统,保证系统正常运行?
□ 系统控制的硬件设备有哪些?
□ 系统需要与哪些其他系统进行交互?
□ 在预定的时刻,是否有事件自动发生?
□ 系统是否需要定期产生事件或结果?
□ 系统如何获取信息?
在寻找系统用户时,建模人员不应把目光只停留在使用计算机的人员身上,而应注意直接或间接地与系统交互或从系统中获取信息的任何人和任何事。在完成参与者的识别后,建模人员就可以从参与者的角度考虑参与者需要系统完成什么功能,从而建立参与者所需要的用例。
一个用例通常要与多个参与者发生交互。其中,不同的参与者所充当的角色不同;有些参与者接收用例所提供的数据,有些参与者则为用例提供某种服务,而另一些参与者要完成系统的管理。这就需要将参与者分类,以保证把系统中所有用例都表示出来。
参与者通常可以被分为主要参与者与次要参与者两类。其中,主要参与者是使用系统较频繁、业务量较大的用户,系统建模人员在识别用例时应该首先识别主要参与者;次要参与者用来给用例提供某些服务。次要参与者与用例进行交互的主要目的是给其他参与者提供所需要的服务,也就是说,次要参与者要使用系统的次要功能。次要功能是指完成系统维护的一般功能。区分主要参与者与次要参与者不应该以参与者使用系统时的权限为依据,一般情况下,应该以使用系统时的业务量为依据。例如,在图书管理系统中,将参与者以主要与次要区分,可以将参与者分成图书管理员和系统管理员。其中,主要参与者负责图书的日常借阅任务,而次要管理者则完成对系统的维护。
除了对参与者进行主次区分外,还可以存在许多其他分类方法。例如,当参与者使用系统时,它们可能会承担着不同的“职责”,建模人员可以利用这些职责来定义参与者与系统间的交互,以及参与者在各种交互中所充当的角色。参与者在系统中的角色主要包括:
□ 系统的启动者。
□ 系统的服务者。
□ 系统服务的接收者。
参与者在系统中所扮演的第一种角色是系统的启动者。启动者是系统的外部实体,它们是为了完成某项事务而启动系统的。一个启动者可以请求某种服务或者触发一个事件。例如,一个使用自动提款机提款的用户就是该系统的一个启动者。
参与者所能承担的第二种角色就是系统服务者,服务者也是系统的外部实体,它们响应系统的请求,为系统提供某种服务。例如,在自动提款机的提款事件中,自动提款机系统需要银行的内部系统提供用户的存款信息。这个银行内部系统就是一个为系统提供服务的参与者。
系统服务接收者的主要职责是接收来自系统的信息。例如,使用自动提款机的用户就是自动提款机系统服务的接收者。从这个示例可以看出,一个人可以在系统中扮演不同的参与者。
参与者的分类方式很多,最终目的就是全面不遗漏地找出参与者。在对参与者建模的过程中,开发人员必须牢记以下几点。
□ 参与者对于系统而言总是外部的,因此它们可以处于人的控制之外。
□ 参与者可以直接或间接地同系统交互,或使用系统提供的服务以完成某件事务。
□ 参与者表示人和事物与系统发生交互时所扮演的角色,而不是特定的人或特定的事物。
□ 一个人或事物在与系统发生交互时,可以同时或不同时扮演多个角色。
□ 每一个参与者需要具有一个与业务一样的名字,在建模中不推荐使用类似于“NewActor”或“新参与者”的名字。
□ 每一个参与者必须有简短的描述,从业务角度描述参与者是什么。
□ 和类一样,参与者可以具有表示参与者的属性和可以接受的事件,但使用得不频繁。
□ 多个参与者之间可以具有与类之间相同的关系。
在完成参与者的识别工作后,建模人员就可以从参与者的角度出发,考虑参与者需要系统完成什么样的功能,从而建立参与者所需要的用例。
3.1.4 用例
用例可以是一组连续的操作,也可以是一个特定功能的模块。系统由一个或多个用例构成,参与者与系统的关系主要表现在参与者与系统用例的关系。用例是一个叙述型的文档,用来描述参与者使用系统完成的事件。
1.用例的概念
用例是用户期望系统具备的功能,它定义了系统的行为特征。用例的目标是要定义系统(包括一个子系统或整个系统)的一个行为,但并不显示系统的内部结构。每个用例说明一个系统提供给它的使用者的一种服务,即一种对外部可见的使用系统的特定方式。它以用户的观点描述用户和系统间交互的完整顺序,以及由系统执行的响应。这里的交互只包括系统与参与者之间的通信,而其内部行为和实现是隐藏的。一个系统的全部用例分割和覆盖它的行为,每个用例代表一部分量化了的、有深刻意义的和对用户可用的功能。
命名用例与命名参与者同样重要。用例名可以是带有数字、字母和除保留符号——冒号以外的任何标点符号的任意字符串。一般情况下,命名一个用例时要尽量使用动词加可以描述系统功能的名词。例如,提取货款、验证身份等用例,其侧重点是目标,而不是处理过程。
在UML中,用例用一个椭圆来表示,用例的名称可以写在椭圆的内部,也可以写在椭圆的外部,但通常情况下是将其名称写在椭圆内部,如下图所示。

需要注意,一定不要在一个用例图中使用两种命名方法,即将用例名写在椭圆之外和椭圆之内。因为这很容易会让模型的读者产生混淆。
一个系统完整的用例描述了该系统的所有行为,这可能导致用例图中的用例非常庞大。为了组织建模信息,UML提供了包的概念,它的功能和目录相似。为了便于使用,可以把一些相关的用例放在一个包中。这样包就变成了包括相关功能的系统的子集。可以通过在用例前面加上包名和两个冒号来确定该用例是属于哪个包的,如下图所示。

注意
用例只会指出系统应该做什么,即系统的需求,而不是确切地说明系统不必做什么,即系统的非功能需求。
2.识别用例
系统分析者必须分析系统的参与者和用例,它们分别描述了“谁来做”和“做什么”这两个问题。
识别用例最好的方法就是从分析系统的参与者开始,对于已经识别的参与者,通过考虑每个参与者是如何使用系统的,以及系统对事件的响应来识别用例。使用这种策略的过程可能会发现新的参与者,这对完善整个系统的模型是有很大帮助的。用例模型的建立是一个迭代过程。
在识别用例的过程中,通过询问下列问题就可以发现用例。
□ 参与者需要从系统中获取哪种功能,即参与者要系统“做什么”?
□ 参与者是否需要读取、产生、删除、修改或存储系统中的某种信息?
□ 系统的状态改变时,是否通知参与者?
□ 是否存在影响系统的外部事件?
□ 系统需要什么样的输入/输出信息?
在用例识别中需要注意以下问题。
□ 用例图中每个用例都必须有一个唯一的名字以区别于其他用例。
□ 每个用例的执行都独立于其他用例。
□ 用例表示系统中所有对外部用户可见的行为。
□ 用例不同于操作,用例可以在执行过程中持续接受或持续输出与参与者交互的信息。
用例的识别也可以通过查找事件的方式来确定,即找出参与者使用系统时的所有操作及获取信息,列为事件表,再根据事件表确定系统用例。
用例图有以下4种标准关系。
□ 泛化关系 参与者间或用例间的关系,类似于继承关系,可以重载。
□ 关联关系 参与者与用例间的关系。
□ 包含关系 用例与用例的关系,将复杂的用例分解成小的步骤用例。
□ 扩展关系 用例间的关系。
3.1.5 关系
这里讲的关系是参与者与用例间的关系,即关联关系。用例图就是描述系统和参与者关系的,而用例和参与者都是独立的事物,关系就是它们之间的关联或通信。这种通信是双向的,参与者肯定要与某个或多个用例交互,用例也肯定会有参与者与之交互,否则参与者或用例将会成为多余。
使用一条实线连接参与者与用例,即可表明它们的关系,如下图表示了一个用例图中的关系。

这个简单的示例只显示了参与者与用例之间的一条通信关联。
不同的参与者可以访问相同的用例,一般来说它们和该用例的交互是不一样的。如果一样的话,那么参与者可能要重新定义。如果两种交互的目的也相同,说明它们的参与者是相同的,可以将它们合并。
用例描述系统满足需求的方式。当细化描述用例操作步骤时,就可以发现有些用例以几种不同的模式或特例在运行,而有些用例在整个执行期间会出现多重流程。如果将用例中重要的可选性操作流程从用例中分隔出来,以形成一个新的用例,这对整个系统的好处是显而易见的。
当分离可重复使用的用例后,用例之间就存在着某种特殊关系。包含和扩展是两个用例紧密相关时关联用例的两种方法。包含关系用于表示用例执行其功能时需要从其他用例引入功能。类似地,扩展关系则表示用例的功能可以通过其他用例的功能得到扩充。
除此之外,用例与用例之间也可以有继承关系,这种关系在用例图中称作泛化关系。在泛化关系中,子用例从父用例处继承行为和属性,还可以添加、覆盖或改变继承的行为,这对后期的开发很有用。