UML 状态图:知识讲解与软件设计师考点¶
状态图描述同一个对象或系统,在生命周期中有哪些状态,以及事件如何引起状态转换。 例如,一个订单从“待付款”变成“待发货”,触发这次变化的是“支付成功”事件。
学习时先抓住一句话:在什么状态下,发生什么事件,满足什么条件,就执行什么操作、进入什么状态。
本文延续软件设计师的备考方向,重点是概念辨认、读图、执行顺序和补图。练习均为自编题,不对应某年真题,不据此推断固定考题数量或分值。
1. 先分清状态、事件和操作¶
| 概念 | 回答的问题 | 订单例子 |
|---|---|---|
| 状态 State | 对象现在处于什么情况? | 待付款、待发货、运输中 |
| 事件 Event | 发生了什么事? | 支付成功、商品发出、客户确认收货 |
| 转换 Transition | 对象从哪里变到哪里? | 待付款 → 待发货 |
| 守卫条件 Guard | 这次转换是否被允许? | 实付金额等于应付金额 |
| 转换效果 Effect | 转换时要执行什么行为? | 记录付款信息 |
“待付款”是状态,“支付成功”是事件,“记录付款信息”是操作。状态名不要写成一串流程步骤,也不要把条件当成事件。
状态既可以是等待,例如“等待输入”,也可以表示正在执行行为,例如“正在下载”。所以“状态必须静止不动”也是错误理解。
2. 用一个订单读懂状态图¶
下面简化为支付、发货、收货和取消几个环节。
stateDiagram-v2
direction TB
[*] --> Unpaid
state "待付款" as Unpaid
state "待发货" as Paid
state "运输中" as Shipping
state "已完成" as Completed
state "已取消" as Cancelled
Unpaid --> Paid: 支付成功(amount) [amount == 应付金额] / 记录付款
Unpaid --> Cancelled: 取消订单 / 记录取消原因
Unpaid --> Cancelled: after(30min) / 标记超时
Paid --> Shipping: 商品发出 / 保存运单号
Shipping --> Completed: 确认收货 / 记录完成时间
Completed --> [*]
Cancelled --> [*]
可以按一个具体实例来读:
- 一个订单进入“待付款”。
- 收到
支付成功(amount)事件,并且金额符合要求,执行“记录付款”,进入“待发货”。 - 收到“商品发出”事件,保存运单号,进入“运输中”。
- 收到“确认收货”事件,记录完成时间,进入“已完成”,随后该状态机执行完成。
取消和超时的箭头都从“待付款”出发,所以不能根据这张图推断订单进入“运输中”后仍能直接取消。箭头的源状态也是转换条件的一部分。
图中的“已完成”“已取消”是带名称的业务状态,末尾的靶心形符号是 UML 的终态。状态机执行完成不代表必须删除订单对象或数据库记录。
这张图没有表达什么¶
它没有规定前端、支付服务、订单服务之间如何调用;那属于顺序图擅长表达的对象交互。它也没有把“校验支付数据、写库、发通知”等处理步骤全部展开;这些步骤可以再用活动图描述。
3. 箭头上的文字怎么读¶
行为状态机的转换标签常写为:
以这条转换为例:
| 部分 | 本例 | 含义 |
|---|---|---|
| 源状态 | 待付款 | 当前状态需要允许这条转换 |
| 触发事件 | 支付成功(amount) | 状态机处理到相应事件,参数可用于条件和操作 |
| 守卫条件 | [amount == 应付金额] |
条件为真,才可能启用这条转换 |
| 转换效果 | / 记录付款 |
真正执行转换时才运行 |
| 目标状态 | 待发货 | 转换后进入的状态 |
标签中的事件、条件、效果可以按需要省略,但省略事件有特定语义,见完成转换。
条件为真,不等于马上跳转¶
支付成功 [金额正确] 同时要求匹配的事件和成立的条件。金额后来变得正确,不会仅因为条件变真就自动重新触发之前那次支付事件。
对没有其他可用转换、没有延期处理等额外规则的简单模型,某次事件到来时守卫为假,该转换就不执行;也不会自动执行斜杠后的操作。
如果需求就是“某条件从假变真时产生反应”,应考虑相应的变化事件,而不是把普通守卫条件当成持续监听器。
事件与条件分别怎么看¶
| 事件类型 | 例子 | 理解方式 |
|---|---|---|
| 调用事件 | submit(order) |
发生了相应操作调用 |
| 信号事件 | paymentSucceeded |
收到相应信号,适合表达异步通知 |
| 时间事件 | after(30min) |
相对时间到达;也可表达指定时刻的时间事件 |
| 变化事件 | when(temperature > 80) |
条件从假变真引发事件 |
在订单例子中,after(30min) 表示相对进入待付款状态的超时事件。它从“待付款”发出,不表示对象此后处于任何状态时都要无条件取消。
同一源状态上如果多条冲突转换被同一个事件启用,而且守卫条件重叠,不能按图上左右位置或文字顺序认定优先选择某一条。建模时应尽量把业务条件写清楚;若题目另给优先级或层次规则,则按题设分析。
4. 状态内部的 entry、do、exit¶
状态框可以写内部行为:
| 写法 | 何时执行 | 注意点 |
|---|---|---|
entry / 行为 |
进入状态时 | 再次进入时会再次执行 |
do / 行为 |
进入状态并完成 entry 后启动 | 可以持续一段时间,也可以提前完成;不是自动无限循环 |
exit / 行为 |
离开状态时 | 与转换箭头上的效果不同 |
对于两个不涉及复合层次的普通状态,执行一条外部转换时,可以记住:
退出源状态时,尚在执行的 do 行为会被中止;上面的顺序不是要求把原 do 完整做完后才能处理所有事件。
例如,A 有 exit / 保存草稿,A 到 B 的箭头上写 提交 / 校验数据,B 有 entry / 显示结果,则相应顺序是:
entry、do、exit 是状态内部行为的特殊标签,不是三个普通的触发事件名称。
5. 自转换与内部转换:最值得辨析的区别¶
两者都可能表现为“最后仍处于同一个状态”,但执行过程不同。
stateDiagram-v2
[*] --> Waiting
state "等待输入" as Waiting
Waiting: entry / 显示输入窗口
Waiting: do / 等待并处理用户输入
Waiting: exit / 关闭输入窗口
Waiting: 查看帮助 / 显示帮助信息
Waiting --> Waiting: 重置 / 清空内容
这里用状态框内的文字展示 UML 内部行为与内部转换的写法;Mermaid 负责绘图,具体语义按以下说明理解。
外部自转换¶
重置 / 清空内容 这条箭头从“等待输入”出发,又回到“等待输入”。本例把它定义为**外部自转换**:
所以“源状态和目标状态一样,就不会执行 exit、entry”是错误的。
内部转换¶
状态框中的 查看帮助 / 显示帮助信息 是内部转换。它处理事件、执行效果,但**不退出、不重新进入这个状态**:
它不会因为这次内部转换而执行“关闭输入窗口”“显示输入窗口”,也不会因为该转换而重新启动原 do。
| 对比项 | 本例的外部自转换 | 内部转换 |
|---|---|---|
| 最后是否仍处于原状态 | 是 | 是 |
| 是否退出再进入 | 是 | 否 |
| 是否执行 exit、entry | 是 | 否 |
| 是否重启 do | 外部自转换重新进入后启动 | 不因这次转换而重启 |
| 典型表示 | 指回自己的外部转换箭头 | 状态框内部的事件/效果条目 |
涉及复合状态时,还存在局部转换 Local Transition 等规则;不能把“局部转换”与这里的“内部转换”直接当成同义词。
6. 没写事件的箭头:完成转换¶
如果一条转换从普通状态出发,没有显式触发事件,它可以是**完成转换 Completion Transition**,由源状态的完成事件隐式触发。
例如:
这里不是“随时只要资料有效就跳过去”,而是:
- 处于“正在校验”,运行内部的检查行为。
- 简单状态的 entry、do 等所需行为完成,产生完成事件。
- 若守卫
[资料有效]为真,执行“保存结果”,进入“校验通过”。
因此:
- 没写事件,不代表任意事件都能触发。
- do 完成,不代表一定离开。 还要看有没有可执行的完成转换。
- 如果完成事件到来时守卫为假,不能指望以后仅凭守卫变真自动重新触发这个完成事件。
- 对复合状态,要进一步检查它的区域是否已经完成。
初始伪状态、选择伪状态等也有不写事件的转换,但它们不应全部被笼统称为“从普通状态发出的完成转换”。要先看箭头的来源。
7. 复合状态与并发区域¶
复合状态:状态里面还有状态¶
“履约中”可以继续分成内部状态。进入内部的“待出库”时,外层“履约中”也处于活动状态。
所以“一个对象任何时候都只能处于一个状态”并不严谨。对于简单、非并发模型,同一层互斥状态中通常只有一个被激活;引入复合状态后,外层与内层可以同时处于活动状态。
正交区域:不同维度可以同时推进¶
例如,订单履约同时包含仓储进度和开票进度,二者是不同维度:
stateDiagram-v2
[*] --> Fulfilling
state "履约中" as Fulfilling {
[*] --> StockPending
state "待出库" as StockPending
state "已出库" as StockDone
StockPending --> StockDone: 出库完成
StockDone --> [*]
--
[*] --> InvoicePending
state "待开票" as InvoicePending
state "已开票" as InvoiceDone
InvoicePending --> InvoiceDone: 开票完成
InvoiceDone --> [*]
}
state "履约完成" as Fulfilled
Fulfilling --> Fulfilled: / 汇总履约结果
Fulfilled --> [*]
虚线分隔的是**正交区域**。进入“履约中”后,可以同时处于仓储区域的“待出库”和开票区域的“待开票”。这不代表订单必须由两个操作系统线程实现。
本例没有外层 do 行为,并以完成转换离开“履约中”,因此两个区域都进入终态后,才执行“汇总履约结果”并进入“履约完成”。只完成开票,不足以走这条外层完成转换。
如果另有从“履约中”出发的取消等事件转换,则要按那条转换分析,不能把“两个区域都完成”误认为所有离开方式都必须遵守的条件。
8. 初始、终态和历史符号¶
| 元素 | 图形/记号 | 含义 |
|---|---|---|
| 普通状态 | 圆角矩形 | 对象在一段时间内所处的情况,可写内部行为 |
| 转换 | 实线箭头 | 从源顶点到目标顶点的转换,可标事件、条件、效果 |
| 初始伪状态 | 实心圆 | 指明进入区域的默认起点;每个区域至多一个 |
| 终态 Final State | 靶心形 | 表示所在区域完成;终态不能再有出边 |
| 选择伪状态 Choice | 菱形 | 到达此处时,根据守卫条件选择后续路径 |
| 浅历史 H | 圆圈内 H | 恢复最近活动的直接子状态;更深层不按深历史恢复 |
| 深历史 H* | 圆圈内 H* | 恢复最近活动的深层状态配置 |
| 终止伪状态 Terminate | 叉号 X | 终止整个状态机执行;不同于仅完成一个区域 |
初始伪状态的出边不能带触发事件或守卫条件,可以指定初始化效果。不要照搬活动图“可有多个初始节点”的规则,说同一个状态机区域也能有多个初始伪状态。
历史节点用来处理“返回复合状态时从哪里恢复”。例如退出时处于“编辑中 → 修改正文”,浅历史恢复“编辑中”,深历史还恢复其内部的“修改正文”状态配置。首次进入、尚无历史记录时,应按模型给出的默认历史路径等规则进入,而不是猜一个以前的状态。
状态图中的终态是区域完成,不能直接套用活动图中“活动终结会终止全部分支”的语义。 顶层多个区域都完成时,才表示整个状态机完成;前面并发示例中的一个区域到达终态,不会因此立即终止另一个区域。
“初始状态”“最终状态”是教材中的常见叫法;严格区分时,初始属于伪状态,Final State 属于终态。
9. 与活动图怎么区分¶
| 对比项 | 状态图 | 活动图 |
|---|---|---|
| 核心关注 | 对象的状态及事件响应 | 一件事的动作步骤、控制流与对象流 |
| 主要节点 | 待付款、待发货、运输中 | 校验库存、生成发票、通知客户 |
| 连线重点 | 事件、守卫条件、转换效果 | 动作之间怎样继续、选择或同步 |
| 同一订单的表达 | 支付成功后,订单变成待发货 | 收到付款后,依次校验、写库、通知仓库 |
| 阅读入口 | 先确定“在描述谁的状态” | 先确定“要完成什么事情” |
两者都属于 UML 行为图,且会使用相似图形。不要仅凭圆角矩形、菱形或靶心就认定图的种类,要看节点含义和整体语义。
配套阅读:UML 活动图与软件设计师考点。
10. 软件设计师读图与补图方法¶
复习时可按以下能力训练,不必把它们当成每年固定出现的题型组合:
| 训练点 | 典型问题 | 判断依据 |
|---|---|---|
| 概念识别 | 哪个是状态、事件、条件或效果? | 代入“当前情况/发生的事/能否转换/执行操作” |
| 路径判断 | 一串事件处理后,最后在哪个状态? | 从当前状态找出边,逐次检查事件与条件 |
| 执行顺序 | exit、转换效果、entry 谁先执行? | 普通外部转换按退出、效果、进入分析 |
| 内部与自转换 | 状态没变,entry 是否仍可能执行? | 看是否真正退出并重新进入 |
| 完成与并发 | 一个 do 或一个区域完成后,能否离开复合状态? | 区分普通事件转换与完成转换所需条件 |
| 根据需求补图 | 缺的是状态名、事件、守卫还是操作? | 用需求里的“处于、收到、如果、执行”逐项对应 |
读图可以固定为四步:
- 确定对象与当前活动状态,复合图还要确定活动的内部状态。
- 找匹配事件的候选转换,再检查守卫,不跳过源状态限制。
- 确定实际转换过程,需要时写出 exit、effect、entry 及 do 的变化。
- 更新状态再处理下一个事件,不要始终在初始状态上读全部事件。
11. 自编练习与解析¶
题 1:拆解转换标签¶
投币(amount) [amount >= 票价] / 打印车票 中,事件、守卫和效果分别是什么?
答案与解析
事件是 投币(amount),守卫是 [amount >= 票价],效果是 打印车票。源状态还必须允许这条转换;不是任何状态收到足额投币都必然打印。
题 2:条件为真就能转换吗¶
订单处于待付款,出边为 支付成功 [金额正确] / 记录付款。现在金额正确,但没有处理到支付成功事件,是否仅凭这个条件就进入待发货?
答案与解析
不能。 这条转换有显式触发事件,普通守卫条件只是启用条件,不能代替事件触发。
题 3:外部转换的执行顺序¶
A 的 exit 输出 X,A 到 B 的转换效果输出 T,B 的 entry 输出 Y。两者都是普通简单状态,不考虑 do 行为。一次该外部转换的输出顺序是什么?
答案与解析
X → T → Y。 先执行源状态退出行为,再执行转换效果,最后执行目标状态进入行为。
题 4:自转换与内部转换¶
状态 S 的 entry 输出 E,exit 输出 X。S 当前已经活动,现在执行效果为输出 T 的外部自转换;如果改成内部转换,两种情况下本次处理分别输出什么?
答案与解析
外部自转换:X → T → E;内部转换:T。 本题只统计这次事件处理,初次进入 S 时已发生的 entry 不重复算入。内部转换不会退出并重新进入 S。
题 5:读订单路径¶
按本文订单图,依次处理金额正确的“支付成功”“商品发出”“取消订单”。最后一次事件没有延期处理、没有图外其他转换。此时订单处于哪里?
答案与解析
运输中。 前两个事件依次使订单进入待发货、运输中;图中的取消箭头只从待付款出发,不能拿来处理运输中的订单。
题 6:并发区域的完成¶
在本文“履约中”示例里,开票区域已进入终态,仓储区域仍在待出库。没有取消等其他离开转换。是否已经可以沿外层完成转换进入履约完成?
答案与解析
不可以。 本例外层完成转换要等两个区域都完成。开票区域的终态只表示该区域完成,不会强行结束仓储区域。
参考资料¶
- OMG UML 2.5.1 标准入口:状态机、状态、伪状态与转换的规范来源。
- UML State Machine Diagrams:状态、内部行为、复合状态、正交区域、伪状态及转换语义。
- 软件设计师:面向对象技术。
- UML 学习资源与路线。
资料用于核对 UML 语义,订单业务和练习为教学示例。查阅日期:2026-09-28。