用例图在一页纸上回答一个问题:谁在使用这个系统,用它做什么?产品经理用它来对齐范围,工程师用它来看清系统的每一个入口,测试人员用它来确认每个目标都有对应的路径。它也是最常被画错的 UML 图之一:有人把它画成流程图,有人把每个按钮都画成一个用例,还有人把 include 和 extend 用反了。
本文按照 UML 2.5.1 规范的定义讲解记法,用一个简单的测试厘清 include 与 extend,完整走一遍订餐应用的示例,展示每个椭圆背后应有的文字描述,并列出让用例图失去价值的常见错误。
记法
| 元素 | 符号 | 含义 |
|---|---|---|
| 参与者 | 带名字的小人 | 与系统交互的角色:人、另一个系统或时间 |
| 用例 | 写有动词短语的椭圆 | 参与者借助系统达成的目标 |
| 主题(系统边界) | 写有系统名称的矩形 | 哪些在系统内,哪些在系统外 |
| 关联 | 实线 | 参与者参与该用例 |
| 包含(include) | 标有 «include» 的虚线箭头,从基础用例指向被包含的用例 | 基础用例总会执行被包含的行为 |
| 扩展(extend) | 标有 «extend» 的虚线箭头,从扩展用例指向基础用例 | 在特定条件下附加到基础用例上的可选行为 |
| 泛化 | 父元素一端带空心三角形的实线 | 特化的参与者或用例 |
箭头方向直接来自 OMG 于 2017 年 12 月发布的 UML 2.5.1 规范:include 从基础用例指向被包含的用例;extend 从扩展用例指向它所扩展的用例。规范还允许使用扩展点,即基础用例中可以接入扩展的具名位置,列在椭圆内部。
include 与 extend:一个测试
问一问:没有另一个用例,基础用例是否完整?
- 没有它就不完整:include。 下单总要付款。“Place order”包含“Pay for order”。箭头指向被包含的用例,基础用例离开它无法执行。
- 没有它也完整:extend。 使用优惠码是可选的,没有优惠码的订单同样完全有效。当顾客有优惠码时,“Apply promo code”在“付款前”这样的扩展点上扩展“Place order”。箭头指向基础用例,而基础用例甚至不知道扩展的存在。
另外两条规则能避免大多数误用:
- 用 include 来共享多个用例都需要的行为,例如身份验证或支付。不要用它把一个用例拆成若干步骤,那是流程图。
- 谨慎使用 extend。如果 extend 比基础用例还多,你记录的是界面选项,而不是目标。
参与者是角色
参与者是角色,而不是某个人或职位。同一个人可以上午是顾客,下午是餐厅经理。参与者也可以是系统:应用为了授权银行卡而调用的支付服务商就是一个辅助参与者。有些团队用一个“Time”参与者来表示定时任务,这是一种常见约定,适用于没有人手动发起的用例。
按照惯例,为达成自身目标而发起用例的主要参与者放在左侧,系统所依赖的辅助参与者放在右侧。
在合适的层级为用例命名
从参与者的视角,用动词加宾语为用例命名:叫“下单”,而不是“订单”“订单页面”或“提交按钮”。每个用例都应该是参与者一次就能完成、并且乐于达成的目标,Alistair Cockburn 称之为用户目标的海平面层级。更高的层级(“经营餐厅”)对一个椭圆来说太宽泛;更低的层级(“输入卡号”)则是用例内部的步骤。
完整示例:订餐
下图展示一个有四个参与者的订餐应用:
- Customer 浏览菜单、下单并跟踪配送。
- Restaurant staff 接单。
- Courier 配送订单。
- Payment provider 是外部系统,为支付提供支持。
边界内有七个用例:
- Browse menu
- Place order,包含 Pay for order
- Pay for order,由支付服务商提供支持
- Apply promo code,在付款前扩展 Place order
- Track delivery
- Accept order
- Deliver order
注意图中没有的东西:没有步骤顺序,没有页面,也没有给每个参与者都画一个“Log in”椭圆。身份验证是前置条件,而不是目标;它应写在用例描述里,只有当它包含实际行为时,才作为一个被包含的用例出现。
每个椭圆背后的描述
图只是索引,真正的价值在于每个用例的简短描述,例如:
Use case: Place order
Primary actor: Customer
Precondition: The customer is signed in and the basket is not empty.
Main success scenario:
1. Customer reviews the basket and delivery address.
2. System shows the total, including fees and estimated delivery time.
3. Customer confirms the order.
4. System takes payment (includes: Pay for order).
5. System sends the order to the restaurant and confirms it to the customer.
Extensions:
2a. Customer has a promo code: Apply promo code, then continue at step 2.
4a. Payment is declined: System asks for another payment method.
5a. Restaurant does not accept within 5 minutes: System cancels and refunds.
Postcondition: The order is paid and visible to the restaurant.
描述中的扩展,也就是那些编号的“如果……怎么办”分支,正是需求和测试用例的来源。它们比图上的 extend 关系详细得多,图上只应画出值得用图形表达的可选目标。
常见错误
- 伪装成用例图的流程图。 用例之间表示“然后”的箭头会把图变成流程模型。表达工作顺序请用 BPMN 图或活动图。
- 每个 CRUD 操作一个用例。 “新建菜品”“修改菜品”和“删除菜品”通常是同一个目标:“管理菜单”。
- 把页面和按钮当成用例。 “点击结算”是步骤,不是目标。
- 用 include 做分解。 如果每个用例都包含另外三个用例,这张图描述的是代码结构,而不是用户目标。
- 把内部组件当成参与者。 数据库不是参与者,它在系统内部。你调用的外部系统才是。
- 没有边界。 没有主题矩形,读者就分不清你在构建什么、又依赖什么。
用例图、用户故事与时序图
| 用例图 | 用户故事 | 时序图 | |
|---|---|---|---|
| 回答的问题 | 谁用系统做什么 | 接下来构建什么,按小块切分 | 在一个场景中各部分如何交互 |
| 粒度 | 用户目标 | 价值增量 | 参与者之间的消息 |
| 最适合 | 范围、入职培训、测试覆盖 | 待办事项规划 | 设计与调试 |
三者相辅相成:用例图描绘全貌,用户故事把它切成可交付的小块,而时序图展示某个场景实际如何运行。
用例图
图表内容
- Food ordering app
- Customer
- Restaurant staff
- Courier
- Payment provider
- Browse menu
- Place order
- Track delivery
- Pay for order
- Apply promo code
- Accept order
- Deliver order
- «include»
- «extend» [has a promo code]
这张图的读法如下:
- 矩形是系统边界:里面的一切都属于订餐应用。
- 左侧的参与者发起用例:顾客有三个目标,餐厅员工和骑手各有一个。
- 右侧的支付服务商是应用在支付时调用的辅助系统。
- 从 Place order 指向 Pay for order 的 «include» 箭头表示支付一定会发生。从 Apply promo code 指向 Place order 的 «extend» 箭头表示优惠码是可选的。
- Markdown 块是给初学 UML 的读者看的记法说明。
画出真正有人读的用例图
- 每张图大约不超过 15 个用例。 如果系统更大,就按参与者或子系统拆分。
- 主要参与者放左边,辅助参与者放右边,让阅读方向与发起顺序一致。
- 明确标注 include 和 extend,带上书名号式的双尖括号,箭头保持虚线。
- 细节留给用例描述。 图应该能放进一张幻灯片,文字则可以按需要写长。
用例图可以与其他 UML 视图搭配使用:领域模型可参考 UML 类图示例,订单的生命周期可参考状态图示例。
在 Archboard 中构建
- 使用 Google 或 GitHub 登录,并新建一张图。
- 添加一个名为 Food ordering app 的基础 框架,作为系统边界。
- 在框架内添加七个 UML 节点包中的 用例 节点,按上文命名。
- 添加四个 UML 节点包中的 参与者 节点:左侧放 Customer、Restaurant staff 和 Courier,右侧放 Payment provider。
- 用普通线条把参与者连接到各自的用例。添加两条虚线连线:一条从 Place order 指向 Pay for order,标注 «include»;另一条从 Apply promo code 指向 Place order,标注 «extend»。
- 添加一个基础节点包中的 Markdown 块,写上简短的记法说明。
这个示例共用 13 个图表节点,在 Free 套餐的限制之内。
常见问题
用例图中 include 和 extend 有什么区别? include 表示基础用例总会执行被包含的行为,箭头指向被包含的用例。extend 表示在特定条件下附加的可选行为,箭头从扩展用例指向基础用例。
参与者可以是系统吗? 可以。任何与系统交互的外部角色都可以是参与者,包括支付服务商这样的其他软件。你系统内部的组件不是参与者。
参与者和用例之间应该用箭头连接吗? UML 在参与者和用例之间使用普通的关联线。有些团队会加上箭头来表示由谁发起,但标准记法并不要求这样做。
一张图应该有多少个用例? 作为参考,大约不超过 15 个。更大的系统拆成几张图会更易读,可以按参与者或子系统划分。
用例图会展示步骤的顺序吗? 不会。它们展示的是目标,而不是顺序。步骤顺序应写在用例描述、活动图或时序图中。