MBSE实践:SysML语言用例建模实例
用例(Use Case)是一种描述系统需求的方法,使用用例的方法来描述系统需求的过程就是用例建模。用例方法最早是由IvaJackboson博士提出的,后来被综合到UML规范之中,成为一种标准化的需求表述体系。在SysML中,我们继续沿用用例。
用例的使用在业界中被推崇备至,整个RFLP流程都被称作是“用例驱动”(Use-CaseDriven)的,各种类型的开发活动包括项目管理、分析设计、测试、实现等都是以系统用例为主要输入工件,用例模型奠定了整个系统产品开发的基础。
1 什么是用例?
在介始用例方法之前,我们首先来看一下传统的需求表述方式—“需求规约”(RequirementSpecification)。传统的需求规约基本上采用的是功能分解的方式来描述系统功能,在这种表述方式中,系统功能被分解到各个系统功能模块中,我们通过描述细分的系统模块的功能来达到描述整个系统功能的目的。一个典型的需求规约可能具有以下形式:
需求规约(RS)
子系统1功能
- 模块1.1功能
- 模块1.2功能
- ...
子系统2功能
- 模块2.1功能
- 模块2.2功能
- …
子系统N功能
- …
采用这种方法来描述系统需求,非常容易混淆需求和设计的界限,这样的表述实际上已经包含了部分的设计在内。由此常常导致这样的迷惑:系统需求应该详细到何种程度?一个极端就是需求可以详细到概要设计,因为这样的需求表述既包含了外部需求也包含了内部设计。在有些公司的开发流程中,这种需求被称为“内部需求”,而对应于用户的原始要求则被称之为“外部需求”。
功能分解方法的另一个缺点是这种方法分割了各项系统功能的应用环境,从各项功能项入手,你很难了解到这些功能项是如何相互关联来实现一个完成的系统服务的。所以在传统的SRS文档中,我们往往需要另外一些章节来描述系统的整体结构及各部分之间的相互关联,这些内容使得SRS需求更象是一个设计文档。
1.1 参与者和用例
从用户的角度来看,他们并不想了解系统的内部结构和设计,他们所关心的是系统所能提供的服务,也就是被开发出来的系统将是如何被使用的,这就用例方法的基本思想。用例模型主要由以下模型元素构成:
- 参与者(Actor)
参与者是指存在于被定义系统外部并与该系统发生交互的人或其他系统,他们代表的是系统的使用者或使用环境。
- 用例(Use Case)
用例用于表示系统所提供的服务,它定义了系统是如何被参与者所使用的,它描述的是参与者为了使用系统所提供的某一完整功能而与系统之间发生的一段对话。
- 通讯关联(Communication Association)
通讯关联用于表示参与者和用例之间的对应关系,它表示参与者使用了系统中的哪些服务(用例),或者说系统所提供的服务(用例)是被哪些参与者所使用的。
这大三种模型元素在UML中的表述如下图所示。

以某实时仿真系统(简称为RS)为例,它的主要功能可以由下面的用例图来表示。RS的主要使用者是仿真操作员,仿真操作员主要使用RS系统进行武器弹道轨迹的仿真和弹道数据处理等。

通讯关联表示的是参与者和用例之间的关系,箭头表示在这一关系中哪一方是对话的主动发起者,箭头所指方是对话的被动接受者;如果你不想强调对话中的主动与被动关系,可以使用不带箭头的关联实线。在参与者和用例之间的信息流不是由通讯关联来表示的,该信息流是缺省存在的(用例本身描述的就是参与者和系统之间的对话),并且信息流向是双向的,它与通讯关联箭头所指的方向亳无关系。
1.2 用例的内容
用例图使我们对系统的功能有了一个整体的认知,我们可以知道有哪些参与者会与系统发生交互,每一个参与者需要系统为它提供什么样的服务。用例描述的是参与者与系统之间的对话,但是这个对话的细节并没有在用例图中表述出来,针对每一个用例我们可以用事件流来描述这一对话的细节内容。如在RS系统中的“选择工作模式”用例可以用事件流表述如下:
选择工作模式-基本事件流
- 仿真操作员输入身份信息
- 仿真操作员选择工作模式
- 启动通讯方式设置面板
- 开启仿真试验模式
- 返回主系统
基本事件流如下图中绿色的活动(Activity)所示

但是这只描述了选择工作模式用例中最顺利的一种情况,作为一个实用的系统,我们还必须考虑可能发生的各种其他情况,如仿真操作员身份鉴权无效、输入密码错、选择工作模式为后置处理等情况,所有这些可能发生的各种情况(包括正常的和异常的)被称之为用例的场景(Scenario),场景也被称作是用例的实例(Instance)。在用例的各种场景中,最常见的场景是用基本流(Basic Flow)来描述的,其他的场景则是用备选流(AlternativeFlow)来描述。对于RS系统中的“选择工作模式”用例,我们可以得到如下一些备选流:
选择工作模式-备选事件流
备选流一:仿真操作员可以在基本流中的任何一步选择退出,转至基本流步骤5。
备选流二:在基本流步骤1中,仿真操作员输入无效身份信息,系统显示错误并允许返回重新身份信息鉴权。
备选流三:在基本流步骤1中,仿真操作员用户输入错误身份信息,系统显示错误并提示用户重新输入身份信息,在给定次数的身份鉴权失败后,用例结束。
备选流四:在基本流步骤2中,仿真操作员选择在选择工作模式时,选择后置处理方式(非实时处理方式),系统进入后置处理设置。
…

通过基本流与备选流的组合,就可以将用例所有可能发生的各种场景全部描述清楚。我们在描述用例的事件流的时候,就是要尽可能地将所有可能的场景都描述出来,以保证需求的完备性。
1.3 用例方法的优点
用例方法完全是站在用户的角度上(从系统的外部)来描述系统的功能的。在用例方法中,我们把被定义系统看作是一个黑箱,我们并不关心系统内部是如何完成它所提供的功能的。用例方法首先描述了被定义系统有哪些外部使用者(抽象成为Actor),这些使用者与被定义系统发生交互;针对每一参与者,用例方法又描述了系统为这些参与者提供了什么样的服务(抽象成为UseCase),或者说系统是如何被这些参与者使用的。所以从用例图中,我们可以得到对于被定义系统的一个总体印象。
与传统的功能分解方式相比,用例方法完全是从外部来定义系统的功能,它把需求与设计完全分离开来。在面向对象的分析设计方法中,用例模型主要用于表述系统的功能性需求,系统的设计主要由对象模型来记录表述。另外,用例定义了系统功能的使用环境与上下文,每一个用例描述的是一个完整的系统服务。用例方法比传统的文字描述的需求规格更易于被用户所理解,它可以作为开发人员和用户之间针对系统需求进行沟通的一个有效手段。
用例被作为整个系统开发流程的基础,很多类型的开发活动都把用例作为一个主要的输入工件(Artifact),如项目管理、架构设计(FL)、验证和确认等。根据用例来对目标系统进行验证和确认,可以根据用例中所描述的环境和上下文来完整地验证和确认一个系统服务,可以根据用例的各个场景(Scenario)来设计测试用例,完全地验证和确认用例的各种场景可以保证测试的完备性。
2 建立用例模型
使用用例的方法来描述系统的功能需求的过程就是用例建模,用例模型主要包括以下两部分内容:
- 用例图(Use Case Diagram)
确定系统中所包含的参与者、用例和两者之间的对应关系,用例图描述的是关于系统功能的一个概述。
- 用例规约(Use Case Specification)
针对每一个用例都应该有一个用例规约文档与之相对应,该文档描述用例的细节内容。
在用例建模的过程中,我们建议的步聚是先找出参与者,再根据参与者确定每个参与者相关的用例,最后再细化每一个用例的用例规约。
2.1 寻找参与者
所谓的参与者是指所有存在于系统外部并与系统进行交互的人或其他系统。通俗地讲,参与者就是我们所要定义系统的使用者。寻找参与者可以从以下问题入手:
- 系统开发完成之后,有哪些人会使用这个系统?
- 系统需要从哪些人或其他系统中获得数据?
- 系统会为哪些人或其他系统提供数据?
- 系统会与哪些其他系统相关联?
- 系统是由谁来维护和管理的?
这些问题有助于我们抽象出系统的参与者。对于RS系统的例子,回答这些问题可以使我们找到更多的参与者:仿真操作员负责使用RS系统,维护人员负责维护和管理RS系统系统、RS系统也需要与目标背景仿真计算机系统及仿真主控计算机系统进行通讯以获得有关武器装备的弹道轨迹相关信息。


系统边界决定了参与者
参与者是由系统的边界所决定的,如果我们所要定义的系统边界仅限于RS系统本身,那么仿真服务器就是一个外部的系统,可以抽象为一个参与者。

如果我们所要定义的系统边界扩大至整个武器弹道轨迹的仿真和弹道数据处理系统,此时目标背景仿真计算机系统及仿真主控计算机系统都是整个RS系统的一部分,这时候目标背景仿真计算机系统及仿真主控计算机系统就不再被抽象成为一个参与者。

值得注意的是,用例建模时不要将一些系统的组成结构作为参与者来进行抽象,如在RS系统系统中,后置处理数据时使用的绘图机只是系统的一个组成部分,不应将它抽象成一个独立的参与者;同时数据库系统往往只作为系统的一个组成部分,一般不将其单独抽象成一个参与者。
2.2 确定用例
找到参与者之后,我们就可以根据参与者来确定RS系统的用例,主要是看各参与者需要系统提供什么样的服务,或者说参与者是如何使用系统的。寻找用例可以从以下问题入手(针对每一个参与者):
- 参与者为什么要使用该系统?
- 参与者是否会在系统中创建、修改、删除、访问、输出和存储武器弹道轨迹数据?如果是的话,参与者又是如何来完成这些操作的?
- 参与者是否会将外部的某些事件通知给该系统?
- 系统是否会将内部的某些事件通知该参与者?
综合以上所述,RS系统的用例图可表示如下,

在用例的抽取过程中,必须注意:用例必须是由某一个参与者触发而产生的活动,即每个用例至少应该涉及一个参与者。如果存在与参与者不进行交互的用例,就可以考虑将其并入其他用例;或者是检查该用例相对应的参与者是否被遗漏,如果是,则补上该参与者。反之,每个参与者也必须至少涉及到一个用例,如果发现有不与任何用例相关联的参与者存在,就应该考虑该参与者是如何与系统发生对话的,或者由参与者确定一个新的用例,或者该参与者是一个多余的模型元素,应该将其删除。
可视化建模的主要目的之一就是要增强团队的沟通,用例模型必须是易于理解的。用例建模往往是一个团队开发的过程,系统架构师或系统工程师在建模过程中必须注意参与者和用例的名称应该符合一定的命名约定,这样整个用例模型才能够符合一定的风格。如参与者的名称一般都是名词,用例名称一般都是动宾词组等。
对于同一个系统,不同的人对于参与者和用例都可能有不同的抽象结果,因而得到不同的用例模型。我们需要在多个用例模型方案中选择一种“最佳”(或“较佳”)的结果,一个好的用例模型应该能够容易被不同的涉众所理解,并且不同的涉众对于同一用例模型的理解应该是一致的。
2.3 描述用例规约
应该避免这样一种误解――认为由参与者和用例构成的用例图就是用例模型,用例图只是在总体上大致描述了系统所能提供的各种服务,让我们对于系统的功能有一个总体的认识。除此之外,我们还需要描述每一个有例的详细信息,这些信息包含在用例规约中,用例模型是由用例图和每一个用例的详细描述――用例规约所组成的。建议每一个用例的用例规约都应该包含以下内容:
- 简要说明 (Brief Description)
简要介绍该用例的作用和目的。
- 事件流 (Flow of Event)
包括基本流和备选流,事件流应该表示出所有的场景。
- 用例场景 (Use-Case Scenario)
包括成功场景和失败场景,场景主要是由基本流和备选流组合而成的。
- 特殊需求 (Special Requirement)
描述与该用例相关的非功能性需求(包括性能、可靠性、可用性和可扩展性等)和设计约束(所使用的操作系统、开发工具等)。
- 前置条件 (Pre-Condition)
执行用例之前系统必须所处的状态。
- 后置条件 (Post-Condition)
用例执行完毕后系统可能处于的一组状态。
用例规约基本上是用文本方式来表述的,为了更加清晰地描述事件流,也可以选择使用状态图、活动图或序列图来辅助说明。如活动图有助于描述复杂的决策流程,状态转移图有助于描述与状态相关的系统行为,序列图适合于描述基于时间顺序的消息传递。
2.3.1 基本流
基本流描述的是该用例最正常的一种场景,在基本流中系统执行一系列活动步骤来响应参与者提出的服务请求。我们建议用以下格式来描述基本流:
1) 每一个步骤都需要用数字编号以清楚地标明步骤的先后顺序。
2) 用一句简短的标题来概括每一步骤的主要内容,这样阅读者可以通过浏览标题来快速地了解用例的主要步骤。在用例建模的早期,我们也只需要描述到事件流步骤标题这一层,以免过早地陷入到用例描述的细节中去。
3) 当整个用例模型基本稳定之后,我们再针对每一步骤详细描述参与者和系统之间所发生的交互。建议采用双向(roundtrip)描述法来保证描述的完整性,即每一步骤都需要从正反两个方面来描述:(1)参与者向系统提交了什么信息;(2)对此系统有什么样的响应。
在描述参与者和系统之间的信息交换时,需指出来回传递的具体信息。例如,只表述参与者输入了反射内存信息就不够明确,最好明确地说参与者输入了反射内存的读取顺序和反射内存的介质模式。通常可以利用词汇表让用例的复杂性保持在可控范围内,可以在词汇表中定义客户信息等内容,使用例不至于陷入过多的细节。
2.3.2 备选流
备选流负责描述用例执行过程中异常的或偶尔发生的一些情况,备选流和基本流的组合应该能够覆盖该用例所有可能发生的场景。在描述备选流时,应该包括以下几个要素:
1) 起点:该备选流从事件流的哪一步开始;
2) 条件:在什么条件下会触发该备选流;
3) 动作:系统在该备选流下会采取哪些动作;
4) 恢复:该备选流结束之后,该用例应如何继续执行。
备选流的描述格式可以与基本流的格式一致,也需要编号并以标题概述其内容,编号前可以加以字母前缀A(Alternative)以示与基本流步骤相区别。
2.3.3 用例场景
用例在实际执行的时候会有很多的不同情况发生,称之为用例场景;也可以说场景是用例的实例,我们在描述用例的时候要覆盖所有的用例场景,否则就有可能导致需求的遗漏。在用例规约中,场景的描述可以由基本流和备选流的组合来表示。场景既可以帮助我们防止需求的遗漏,同时也可以对后续的开发工作起到很大的帮助:开发人员必须实现所有的场景、测试人员可以根据用例场景来设计测试用例。
2.3.4 特殊需求
特殊需求通常是非功能性需求,它为一个用例所专有,但不适合在用例的事件流文本中进行说明。特殊需求的例子包括法律或法规方面的需求、应用程序标准和所构建系统的质量属性(包括可用性、可靠性、性能或支持性需求等)。此外,其他一些设计约束,如操作系统及环境、兼容性需求等,也可以在此节中记录。
需要注意的是(敲黑板),这里记录的是专属于该用例的特殊需求;对于一些全局的非功能性需求和设计约束,它们并不是该用例所专有的,应把它们记录在《补充规约》(作者注:RUP中的工件)中。
2.3.5 前置和后置条件
前置条件是执行用例之前必须存在的系统状态,后置条件是用例一执行完毕后系统可能处于的一组状态。
2.4 检查用例模型
用例模型完成之后,可以对用例模型进行检查,看看是否有遗漏或错误之处。主要可以从以下几个方面来进行检查:
- 功能需求的完备性
现有的用例模型是否完整地描述了系统功能,这也是我们判断用例建模工作是否结束的标志。如果发现还有系统功能没有被记录在现有的用例模型中,那么我们就需要抽象一些新的用例来记录这些需求,或是将他们归纳在一些现有的用例之中。
- 模型是否易于理解
用例模型最大的优点就在于它应该易于被不同的涉众所理解,因而用例建模最主要的指导原则就是它的可理解性。用例的粒度、个数以及模型元素之间的关系复杂程度都应该由该指导原则决定。
- 是否存在不一致性
系统的用例模型是由多个系统架构师或系统工程师协同完成的,模型本身也是由多个工件所组成的,所以我们要特别注意不同工件之前是否存在前后矛盾或冲突的地方,避免在模型内部产生不一致性。不一致性会直接影响到需求定义的准确性。
- 避免二义性语义
好的需求定义应该是无二义性的,即不同的人对于同一需求的理解应该是一致的。在用例规约的描述中,应该避免定义含义模糊的需求,即无二义性。