软件分析与设计笔记 08:功能需求(一):用例模型与活动图

从用户故事到用例的翻译过程:什么才算「好」的参与者,系统边界怎么划,用例描述必须写清哪四件事,活动图怎么把分支、并行与泳道画清楚,以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。

学习#需求分析#用例建模#UML#绘图#学习笔记

本文是 《软件分析与设计笔记》 系列的第 8 篇,共 28 篇

这是我在学习软件分析与设计时整理的系列笔记。概念部分是我按自己的理解写的,示例与图示都是自己编、自己画的。理解有偏差的地方欢迎指出。

本系列的贯穿案例:片区家电维修派单系统 —— 客户报修 → 客服派单 → 工程师接单上门 → 回填工单 → 仓管备料 → 片区经理看板。角色:客户、客服、工程师、仓管、片区经理,以及外部的「配件供应商库存系统」。这一篇从这里开始,后面 09、10、12、14、18、21、22、27 用的都是它。

0 故事写完,开发还是问你一堆问题

十条用户故事摆在开发面前,第一个问题通常是:

「工程师到了现场,发现配件没有货,这单怎么办?」

故事里没写。因为用户故事天生只描述从使用者视角的一句话,它不负责回答「系统内部怎么处理各种情况」。

功能性需求(functional requirement)要把这一层补上,需要换一种描述方式:用例(use case)。

1 从用户故事到用例

1.1 两种视角的差别

两者的视角不一样:

用户故事用例
视角使用者的诉求系统与外部的一次完整交互
粒度一句话一段可展开的流程
关注点为什么做系统该做什么、按什么顺序做

1.2 三种翻译情况

翻译时有三种常见情况:

  • 一对一 —— 一条故事正好对应一个用例,例如「客户报修」;
  • 多对一 —— 几条故事合成一个用例,例如「查询配件库存 / 查看可约时段 / 确认上门时间」最终都是「安排上门」这个用例的步骤;
  • 一对多 —— 一条大故事拆成几个用例,例如「派单」会拆成「创建工单」「分配工程师」「通知客户」三个。

判断标准是:一个用例应该有一个明确的目标达成点。「工单建好了」是一个目标,「客户知道几点上门」是另一个。

2 什么才算「好」的参与者

2.1 三个判断要点

把所有参与者与用例画在一起,就是用例图(use case diagram);图加上每个用例的文字描述,构成完整的用例模型(use case model)。其中的基本单元是参与者(actor)—— 与系统交互的外部角色。三个判断要点:

  1. 是唯一的角色,不能是泛称。「用户」不算一个 actor,「客户」「客服」「仓管」才是。因为不同的角色关心的东西不一样;
  2. 可以是人,也可以是外部系统。 在贯穿案例里,「配件供应商库存系统」就是一个 actor —— 系统要去查它的库存;
  3. 判断依据是目标,不是身份。 问一句:这个角色在系统里有没有自己要达成的目标?没有的就不是 actor,只是流程里的一个步骤。

第 3 条能过滤掉很多噪音。比如「打印机」不是 actor(它没有目标),但「片区经理」是 —— 他要看的看板和工程师要看的不一样。

用例图与活动图共十四个符号的特写:左列是用例图的参与者、用例、系统边界、关联线,以及 include、extend、泛化三种关系的箭头方向;右列是活动图的开始状态、结束状态、动作、转移、判断菱形、同步条、泳道。每个符号下方标出 draw.io 形状库里的搜索关键词

上面这张图把这两个图种要用的符号一次列全,下面每讲到一处,都可以回来对照它在 draw.io 里搜什么。

画这一笔:draw.io 左侧搜索框输入 actor,把火柴人拖进画布,双击图中默认的 Actor 字样改成 Customer。 想快速复制同类角色:选中火柴人按 Ctrl+D(复制),再双击改名。

2.2 一个角色还是一类角色

贯穿案例里有个真实的判断:「普通客户」和「买了延保的客户」要不要建成两个 actor?

判断方法是问一句:他们做的事一样吗?

  • 一样,只是待遇不同(延保客户免费换件)→ 合并成一个 actor,差异写成业务规则,放到活动图的分支里;
  • 不一样,能用的功能都不同(延保客户能自助申请换件)→ 拆成两个 actor。

这个判断会直接改变用例的个数,所以要早做。

两个参与者连到同一个用例的三种画法:第一种是一条用例连两条实线,适用两人触发与执行方式完全一致的情况;第二种是参与者泛化,父角色挂公共用例、子角色只挂差异用例,适用共用的是一整批用例;第三种是拆成两个用例,适用两人用法不同。右下角给出判断方法:问这两个角色点进去看到的是不是同一个界面、走的是不是同一套步骤

2.3 两个 actor 连同一个用例:可以,但有前提

一个用例连两个火柴人,是完全合法的画法 —— 它表示这两个角色触发并执行这个用例的方式完全一致。

注意最后半句。讲义的原话是:

both will trigger and execute the use case in the same way

它不是「两个角色都会用到这个功能」,而是「两人点进去之后看到同一个界面、走同一套步骤」。这两件事差很远。

本例里正好有一对可以对照:

一对角色用法一致吗该怎么画
客服 与 片区经理「看待派单」「看排期」「改约」三个人事完全一样,经理只是多了「看报表」一条用例连两条线(或做参与者泛化)
客服 与 工程师客服是「派单」,工程师是「接单」—— 动作方向都不同拆成两个用例(Assign Engineer / Accept Assignment)

第二行是很多人会画错的地方:两个用例都跟「派单」有关,看起来像一个东西的两面,但用法完全不同 —— 所以必须分开画。

判断方法一句话:

这两个角色点进去之后,看到的是同一个界面、走的是同一套步骤吗? 是 → 一个用例连两根线;不是 → 拆成两个用例,哪怕名字听起来像。

讲义里还有一条方向相反、同样重要的提醒:「好的 actor = 唯一角色」。两组人扮演同一个角色时可以合并;但如果他们将来会访问不同的用例,就该保持分开。也就是说 —— 能不能共用一个用例,由「用法是否一致」决定,不由「是不是同一批人」决定。

2.4 共用的三种画法,什么时候用哪种

画法什么场景用图上的样子
一条用例 + 两条关联线首选。两人只在这一个用例上用法一致两个火柴人各拉一条实线到同一个椭圆
参与者泛化两人共用的是一整批用例,只是权限不同父角色(抽象)挂公共用例,子角色只挂差异用例,空心三角子→父
拆成两个用例两人用法不同两个椭圆,各连各的 actor

第一种和第二种怎么选,看共用的是一个还是一批:只共用一个,直接连两根线最省事;共用一整批,泛化更清楚(泛化会在笔记 10 里展开)。

画这一笔:draw.io 里直接从第二个 actor 拖一条实线到这个已有椭圆即可,不需要新建图形。 注意不要画箭头 —— 关联线表示 actor 主动触发用例,带箭头只在「系统主动推送」(比如定时提醒)时才用。

3 系统边界:哪些交互进系统

3.1 边界怎么划

用例图画在一个系统边界(system boundary) 里。边界怎么划,是这一步最需要讨论的地方。

看贯穿案例里的一个常见分歧:「给客户发上门短信」这件事,在系统边界内还是外?

判断方法:这个动作需不需要系统保存状态、做判断?

  • 「决定该给谁发、什么时候发、发什么内容」需要保存工单状态、判断工程师有没有出发 —— 在边界内,是一个用例;
  • 「通过哪个短信服务商把短信发出去」是纯粹的通道 —— 在边界外,它是一个外部系统 actor。

边界划错会带来两种后果:划得太宽,需求里塞满了技术选型;划得太窄,该由系统负责的判断被漏掉了。

画这一笔:draw.io 搜 boundary(UML 分类下),拖入画布拉成一个大矩形,所有用例都放进去。 actor 必须留在框外 —— 框里只放椭圆。这一条阅卷人扫一眼就能判断。

3.2 边界画完之后

边界确定后,把落在边界内的用例一个个列出来。贯穿案例的第一版清单大致是:

text
客户      :报修、查询上门进度、评价服务
客服      :创建工单、分配工程师、改派
工程师    :接单、回填工单、申请备料
仓管      :出库、归还余料
片区经理  :查看派单看板、查看工程师工作量
供应商系统:查询配件库存(系统调用)

这份清单还不是图,只是画图前的材料。下一步才是把它画出来。

4 自动化到什么程度:搬流程还是改流程

边界划完之后,还有一个更根本的问题:这一段流程要不要原样保留?

  • digital automation(数字化自动化) —— 把现有手工作业 1:1 搬进系统。风险小,但会把现有流程里的低效也一起固化下来;
  • digital transformation(数字化转型) —— 借这个机会重新设计流程本身,做的不是「原来的事换个工具」,而是「换一种做法」。

判断工具就是一句提问:「这个动作存在的理由是什么」。贯穿案例里,现在客服要「打电话问工程师今天有没有空」,问一句为什么 —— 答案往往是「因为不知道排期」。真正的目的(知道谁有空)完全可以通过系统排期实现,那这一步本身就不需要保留。

这个决定要早做,因为它直接改变用例的个数。

5 用例描述必须写清的四件事

只画图不够,每个用例都要配一段文字描述(use case description),至少包含:

要素内容
参与者(actor)谁发起这次交互
前置条件(precondition)开始之前必须已经成立的状态
主流程(basic flow)一切顺利时的步骤序列
例外流程(alternative flow)出错、取消、超时、数据缺失时怎么办

前置条件常常被忽略。贯穿案例里,「回填工单」的前置条件是「工程师已到现场且工单状态为进行中」—— 没有这一句,讨论就不知道从这里开始。与之对应的后置条件(postcondition)则写明结束后应当成立的状态,例如「工单状态变为待结算,配件消耗已记录」。

例外流程才是真正的价值所在。主流程只占一个用例的 30%,剩下 70% 的工作量都藏在例外里。 比如「申请备料」的例外是「仓库也没有这个配件」,这时整单要转成「待件挂起」,客户要收到改约通知 —— 这条路径如果没在用例里写出来,开发根本不会做。

6 活动图:把流程画到看得见分支

6.1 三类结构

用例描述写清楚了,还需要一种图形化的表达方式,让业务方也能看懂 —— 这就是活动图(activity diagram)。

一张活动图里最该出现的是这三类结构:

  • 判断节点(decision node)与汇合节点(merge node) —— 根据条件走不同分支,再合回一条路径。贯穿案例里最典型的是「是否在保修期内」,两条路径的处理完全不同;
  • 并行(fork / join) —— 两件事同时进行。比如「通知客户」和「占用工程师时段」可以并行,不必等对方;
  • 泳道(swimlane) —— 用竖直分区把不同角色负责的步骤分开。

画这一笔:draw.io 里这三个形状分别是 —— 判断用 decision(菱形),并行用 fork(粗黑横线,搜 horizontal bar),泳道搜 lane(竖长条)或直接手画矩形 + 分隔线。 判断的分支条件要写在方括号里([yes] [no]),draw.io 里是双击连线中间的位置加文字,不是拖一个文本框。

6.2 泳道为什么最实用

泳道是最实用的一类。它一眼就能看出「这一步是谁在做」—— 如果一条流程在活动图上反复跨越泳道,通常说明这个流程本身有协调成本。

贯穿案例里有个很好的对比:「工程师申请备料」这件事,如果画出来是在工程师泳道和仓管泳道之间来回跨四次(申请 → 查库存 → 缺货 → 改约 → 通知),那这条流程的协调成本就很高。看到这种图,业务方自己就会说「能不能简化成一次申请」。

画这一笔:泳道在 draw.io 里没有自动吸附。最省事的做法是手画:拖 N 个等宽矩形横排,顶部写角色名,然后所有活动框都放进对应的矩形里。 缺点是一移动就乱。所以先把活动位置排好,最后再画泳道框。

6.3 图之外还要一张表

除了图形,实际交付时往往还会配一张活动流程表(activity flow table):每一行是一个步骤,列出执行者、动作、条件、输出。图形负责沟通,表格负责精确。

一个例子(截自「申请备料」这一段):

活动执行者条件说明
提交备料申请工程师工单状态 = 进行中填写配件编号与数量
查询库存系统—调供应商库存接口
库存充足仓管库存 ≥ 申请量生成出库单
库存不足系统库存 < 申请量工单转「待件挂起」,通知客户改约

6.4 把实体挂到活动图上

考试里活动图那道题通常会加一句「include entities」。这不是让你多画几个框,而是要求这张图同时表达流程和数据两件事 —— 只画了动作和分支,等于只答了一半。

做法是给每个动作用到的业务实体系一条线。方向取决于读还是写,这是这一节唯一的难点:

这一步在做什么箭头方向例子
读数据(查、取、显示)实体 → 动作「查询工单状态」← WorkOrder
写数据(新增、更新、删除)动作 → 实体「回填工单」→ WorkOrder

一个记法:数据流向动作就是读,动作流向数据就是写。看起来像废话,但画的时候极容易顺手都画成同一个方向。

实体本身怎么挑,还是那三条标准(笔记 09):业务概念而不是动作、需要长期记住、有多个实例。在贯穿案例里,WorkOrder、PartUsage、StockItem 都是实体;「提交申请」不是。

画这一笔:实体用普通矩形(或搜 class 取一个空的三格框),直接放在对应泳道的右侧。连线时按上表定方向,标 read / write 在线上更清楚。 不必给每个动作都挂实体 —— 挂那些真正读写数据的动作就够了,否则图会糊成一团。

6.5 自查这张活动图

画完先过一遍这五条,能挡住大部分低级失分:

  • 只有一个开始状态,且每个分支最终都能走到结束状态?
  • 每个判断的出口都标了方括号条件?
  • 泳道数量与参与角色数量对得上?
  • 需要实体的动作都挂了实体,读写方向没画反?
  • 没有画「是否输入有误」「操作成功」这类校验与确认框?(这一阶段应忽略异常)

7 为什么值得画活动图

三个具体收益:

  1. 逼出例外流程。 画到分支处,必须回答「另一种情况怎么办」;
  2. 发现缺失的步骤。 从「客户点了提交」到「系统提示成功」之间,通常还漏着好几步;
  3. 可以直接拿去和用户核对。 一张图比一段文字更容易被指出错误 —— 「这里不是这样的」,这是需求验证最有效的时刻之一。

8 用例按什么打包

8.1 三种打包依据

用例多了以后需要分组,也就是划分用例包(use case package)。常见的依据有三种:

  • 按业务主题(package by domain) —— 按业务范围归类,例如「报修相关」「备料相关」;
  • 按角色 —— 每个角色一包,便于按岗位做需求确认;
  • 按流程阶段 —— 对应业务的阶段划分,例如「上门前」「上门中」「上门后」。

贯穿案例按第一种分成三包比较自然:

text
报修与派单   :报修、创建工单、分配工程师、改派
上门服务     :接单、回填工单、申请备料
结算与看板   :评价、结算、派单看板、工作量统计

打包的意义不只是整理。它同时也是在回答「谁该来确认这一包需求」:按角色打包的,就该由对应角色确认。

本节自测

  1. 用户故事和用例的视角差别是什么?
  2. 判断一个角色是不是 actor,看的是身份还是目标?
  3. 贯穿案例里「给客户发上门短信」这件事,哪部分在系统边界内,哪部分在外?
  4. 为什么说用例的价值主要在例外流程里?举一个本例中的例外。
  5. 活动图里的泳道解决了什么问题?流程反复跨越泳道说明什么?
  6. 在 draw.io 里画泳道,为什么建议「最后再画泳道框」?
  7. 「普通客户」和「延保客户」,什么情况下合并成一个 actor,什么情况下拆成两个?
  8. 用例包按什么依据划,会直接影响哪件事?

参考答案

  1. 视角差别:用户故事是「用一句话说清谁要什么」,用例是「系统与外部的一次完整交互」。前者回答为什么做,后者回答系统做什么、按什么顺序做(§1.1)。
  2. 看目标。 判断标准是「这个角色在系统里有没有自己要达成的目标」。所以他是不是人、什么职位都不重要 —— 打印机没目标,不是 actor;供应商库存系统有目标(被查询),是 actor(§2.1)。
  3. 分两段看:决定发给谁、何时发、发什么内容 → 在边界内(要保存工单状态、做判断);通过哪个短信服务商发出去 → 在边界外(纯通道,是外部系统 actor)(§3.1)。
  4. 因为主流程只占 30%,例外里藏着大部分真实工作量。本例的典型例外:「申请备料时仓库也没有这个配件」→ 工单转「待件挂起」→ 通知客户改约 → 重新排期。这条路径不写出来,开发就不会做(§5)。
  5. 泳道解决「这一步是谁在做」,让人一眼看出责任归属。反复跨越泳道说明这个流程的协调成本高 —— 本例「申请备料」跨四次就是个信号,业务方看到会想简化(§6.2)。
  6. 因为 draw.io 的泳道框不会自动吸附活动框,一移动就乱。先排好活动位置、再画泳道框,可以少返工(§6.2 的画这一笔)。
  7. 合并:两人做的事一样、只是待遇不同(延保客户免费换件)→ 差异写成业务规则,放进活动图分支;拆分:能用的功能都不同(延保客户能自助申请换件)→ 各自要有独立用例(§2.2)。
  8. 按什么依据打包,直接决定「谁该来确认这一包需求」。按角色打包就由对应角色确认,按阶段打包就由该阶段的负责人确认(§8.1)。

术语表

术语中文一句话解释
functional requirement功能需求系统要「做什么」
use case用例一个参与者通过系统达成某个目标的完整交互
use case model用例模型参与者与用例的集合,描述系统的功能边界
use case diagram用例图画出参与者、用例与系统边界的图
actor参与者与系统交互的外部角色,可以是人或外部系统
system boundary系统边界区分「系统负责」与「外部负责」的那条线
use case description用例描述用文字写清一个用例的流程与条件
basic flow主流程一切顺利时的步骤序列
alternative flow例外流程出错、取消、超时等情况下的处理
precondition前置条件用例开始前必须成立的状态
postcondition后置条件用例结束后应当成立的状态
activity diagram活动图描述流程步骤、分支与并行的图
activity flow table活动流程表用表格逐行记录步骤、执行者与条件
decision node判断节点按条件分叉的节点
merge node汇合节点把多个分支重新合到一起的节点
fork / join分叉 / 汇合表示并行开始的节点 / 等待并行结束的节点
swimlane泳道用分区表示每个步骤由谁负责
use case package用例包按主题、角色或阶段对用例分组
digital automation数字化自动化把现有手工流程原样搬进系统
digital transformation数字化转型借系统建设重新设计流程本身

© 2026 HuanYu Blog

记录技术,也记录思考