完整逐步教程:如何创建状态机图(适用于绝对初学者)

一个UML 状态图是一种强大的可视化工具,通过展示系统在事件触发下如何在不同状态之间转换,来模拟系统的动态行为。它捕捉了对象或过程的生命周期——展示它可能处于的状态处于,是什么触发了变化,以及状态变化过程中发生哪些动作——使其非常适合理解复杂的系统,如交通灯、自动售货机、登录流程或游戏角色。通过关注状态(如“红灯”、“等待付款”或“跳跃”)、转换(由事件驱动,如“计时器到期”或“按钮按下”)以及条件(保护条件),状态图提供了清晰性,防止逻辑漏洞,并成为设计和代码的基础。无论你是初学者学习系统建模,还是开发者构建稳健的软件,掌握状态图都能让你以精确和清晰的方式思考、设计和沟通系统行为。

State Machines for Everyone — Part 1 Introduction | by Alex Dodge | Well  Red | Medium

💡 目标:学习如何使用状态机对现实世界系统进行建模——从构思到清晰、专业的图表。

🔑 你必须首先理解的关键概念

概念 它的含义 为什么重要
状态 系统所处的一种条件或情况(例如红灯等待投币) 展示任何时刻正在发生的情况
事件 引发变化的某种事物(例如投入硬币计时器到期) 导致状态之间的转换
转换 从一个状态指向另一个状态的箭头 通过事件连接状态
初始状态 起点 (●) 总是有一个
最终状态 过程结束 (○) 可选——并非总是需要
守卫 [条件] 转换发生的前提条件必须为真 添加逻辑(例如:钱够吗?)
动作 / 入口/执行 进入、处于或退出某个状态时会发生什么 为状态添加行为

📌 思考:
“这个系统可以处于 X 种状态。
当 Y 发生时,它会转移到 Z.”
这就是一个状态机!


🛠 第0步——思维模式:提出这些问题

在画任何东西之前:

  • 这个事物可能处于的 明显不同的状态 有哪些?

  • 什么 事件(用户操作、时间、错误)会导致变化吗?

  • 它能同时处于两种状态吗?(不行 → 基本状态机是互斥的。)

👉 示例:一个灯的开关要么是或者绝不会同时两者都有。


🧩 第一步 – 选择一个具体的对象进行建模

✅ 适合初学者的良好选择:

  • 闸机(锁定/解锁)

  • 交通灯(红/绿/黄)

  • 自动售货机

  • 登录系统

  • 订单状态:已创建 → 已支付 → 已发货 → 已送达

❌ 避免:

  • “整个在线商店” → 太大了

  • “用户体验” → 太模糊

✏️ 从简单开始。先掌握小例子。


📌 第二步 – 列出状态(使用名词或现在分词)

写下4到8个现实的状态.

使用形容词或现在分词来使其看起来像一种状态:

  • 红色

  • 绿色

  • 黄色

  • 等待硬币

  • 发放物品

  • 准备中

  • 支付失败

✅ 提示:如果你有超过10个状态 → 将系统拆分为更小的部分。


🖌 步骤3 – 将状态绘制为圆角矩形

使用圆角矩形:

[ 红色 ]
[ 绿色 ]
[ 等待硬币 ]

✅ 工具:

  • draw.io / diagrams.net(最佳免费选择)

  • Excalidraw(手绘风格)

  • PlantUML(基于文本 → 易于版本控制)

  • Lucidchart / Miro


🔷 步骤4 – 添加初始状态(黑色圆点)

绘制一个实心黑色圆圈并用箭头指向第一个状态。

[*] --> 红色

这个[*]表示“初始状态”——它是起点。


➡️ 步骤5 – 使用事件绘制转换

针对每个状态,问自己:

“这里会发生什么事件,让我离开这个状态?”

用以下内容标注箭头:

事件 [守卫] / 操作

🔹 从简单开始:只需事件事件 / 操作

常见事件:

  • 投入硬币

  • 计时器超时

  • 支付失败

  • 按钮被按下

  • 行人按钮

  • 超时


✅ 第6步 – 添加最终状态(可选)

使用一个带有粗边框的圆圈作为最终状态。

[已交付] --> [●]

并非所有系统都有最终状态(例如永远运行的交通灯)。


🔁 第7步 – 添加现实中的边缘情况

询问:

  • 可以取消吗?→ 添加取消→ 返回到空闲

  • 时间会耗尽吗?→超时→ 返回到等待

  • 它会失败吗?→ 添加错误 → 返回开始

  • 它可以保持在同一状态吗?→自转换

示例:自转换(增加更多钱):

[有信用] -- 投入硬币 --> [有信用]

🚦 第8步 – 使用守卫实现智能逻辑

同一个事件导致不同的结果,使用守卫.

示例:

如果你按下pedButton绿灯,但目前还没有需求 → 你进入绿灯且行人等待.

但如果需求已经设定 → 你只需忽略它。

[车辆绿灯] --> [车辆绿灯] : pedButton / set demand = true

这是一个带动作的自转换—— 不是新状态。


🎯 第9步 – 添加入口/执行/退出动作(可选但功能强大)

您可以在状态框内编写动作在状态框内:

[红色]
入口 / 打开红色
退出 / 关闭红色
执行 / 等待30秒

有助于明确行为,而不会使转换变得杂乱。


✅ 第10步 – 最终检查清单(请自问)

✅ 检查 为何重要
只有一个初始状态吗? 必须从某个地方开始
所有状态都有外出箭头(最终状态除外)? 没有死胡同
没有无法到达的状态吗? 每个状态都应该是可到达的
转换是否用事件标记? 清晰的因果关系
箭头不表示“前往X”——箭头仅表示方向 更简洁
是否包含了取消/超时/错误路径? 真实系统会失败——请做好准备
图表是否能完整显示在屏幕上? 清晰且易读

📋 快速参考:PlantUML 语法(UML 标准)

符号 含义
[*] 初始状态
[*] --> 状态 从此状态开始
状态 --> 状态 转换
事件 [保护条件] / 动作 箭头上的标签
状态 "名称" 命名状态(可选)
状态 "X" 作为 X 复杂名称的别名
状态右侧的注释 注释框

🎯 示例 1:简单的交通灯(三状态循环)

非常适合完全的初学者。

🧠 现实世界应用:

  • 基本交通灯循环:红 → 绿 → 黄 → 红

✅ 状态:

  • 绿

🔄 事件:

  • 计时器到期(30秒后,25秒后,5秒后)

🛠 PlantUML 代码(可复制粘贴):

@startuml
skinparam monochrome true
[*] --> 红
红 --> 绿 : after(30s)n计时器到期
绿 --> 黄 : after(25s)n计时器到期
黄 --> 红   : after(5s)n计时器到期

红   : entry / 开启红灯
绿 : entry / 开启绿灯
黄: entry / 开启黄灯

note right of 红
  车辆必须停下
end note

note right of 绿
  车辆可以通行
end note

note right of 黄
  准备停下
end note
@enduml

✅ 如何使用:
前往 https://www.plantuml.com/plantuml,粘贴代码,然后点击“生成”。

🖼️ 输出:一个干净、带有动画效果的状态机图。


🎯 示例 2:带行人请求的逼真交通灯

最具教育意义的版本—— 引入了守卫、自转换和复杂逻辑。

🧠 现实世界应用:

  • 行人按下按钮以过马路。

  • 如果有人在等待,信号灯会停留更长时间。

  • 绿灯结束后,进入黄灯 → 红灯 → 允许通行 → 闪烁的禁止通行 → 回到绿灯。

📌 关键状态:

  1. 车辆绿灯_无需求—— 绿灯,无行人等待

  2. 车辆绿灯_行人等待—— 绿灯,有人按了按钮

  3. 车辆黄灯—— 黄灯(禁止通行)

  4. 全红—— 安全缓冲(极短)

  5. 行人通行—— 允许通行标志亮起

  6. 行人清空—— 闪烁的禁止通行(清空时间)


🧩 关键转换:

  • 行人按钮—— 如果未等待 → 设置需求

  • 计时器到期—— 转为黄灯(如果绿灯时间已满足)

  • 行人按钮—— 在黄灯/红灯期间 → 记住需求

  • 计时器行走 → 转到闪烁的禁止行走

  • 计时器清空 → 重置并返回绿色

🚨 注意:此版本使用守卫和自转换,展示为什么状态机功能强大.


✅ PlantUML 代码(完全可用,可直接使用):

@startuml
skinparam monochrome true
skinparam shadowing false
skinparam dpi 120

[*] --> VehicleGreen_NoDemand

state "车辆绿灯n(无行人需求)" as VG_No
state "车辆绿灯n(行人等待)" as VG_Wait
state "车辆黄灯" as VYellow
state "全红n(安全缓冲)" as AllRed
state "行人通行" as PedWalk
state "行人清空n(闪烁禁止通行)" as PedClear

VG_No --> VG_Wait : pedButton / setPedDemand = true
VG_No --> VYellow : after(35s)nor (pedDemand && minGreenTimeMet)
VG_Wait --> VYellow : after(45s)n行人等待时延长绿灯
VG_Wait --> VG_Wait : pedButton / 忽略(已等待)
VYellow --> AllRed : after(4s)
AllRed --> PedWalk : after(1s)
PedWalk --> PedClear : after(10s)n通行时间结束
PedClear --> VG_No : after(5s)n清空完成n/ resetPedDemand

note bottom of VG_No
  正常运行
  无行人需求
end note

note right of PedClear
  行人完成过街
  闪烁禁止通行标志
end note

note right of VG_Wait
  行人按下按钮
  绿灯最多延长10秒
end note

note right of VYellow
  准备停止
  车辆信号灯切换
end note

note right of PedWalk
  通行标志亮起
  行人可过街
end note
@enduml

💡 为什么这个版本比简单版本更好?

  • 展示现实世界的复杂性

  • 展示守卫 (如果行人需求)

  • 使用自转换 (VG_Wait --> VG_Wait)

  • 建模真实行为:绿灯可以延长!

  • 清晰的分离车辆行人逻辑


🎓 推荐练习题(按顺序完成)

# 示例 时间 学到的技能
1 灯开关(开 ↔ 关) 5分钟 基本转换
2 闸机(锁定 ↔ 解锁) 10分钟 事件,守卫
3 交通灯(三状态循环) 10分钟 定时器,进入动作
4 自动售货机(等待 → 支付 → 发放) 15分钟 多个事件,金钱逻辑
5 登录(空 → 输入 → 提交 → 成功/失败) 15分钟 错误处理,最终状态
6 订单状态(6种状态) 20分钟 现实生活中的系统建模

✅ 从第1–3项开始在纸上或draw.io中进行。然后使用 PlantUML 完成其余部分。


🧠 成功的最后建议

  • 从小处着手 —— 不要试图一次性包含所有内容。

  • 使用真实名称 — 等待硬币,而不是 状态1.

  • 清晰地标记转换 — 按钮按下超时支付失败.

  • 先用手画出来 —— 然后数字化。

  • 在脑海中测试它:“这个系统会卡住吗?” → 如果是,添加一个转换。


📌 总结:你的状态机检查清单

✅ 一个 [*] (初始状态)
✅ 圆角矩形表示状态
✅ 箭头表示转换
✅ 箭头上标注事件(30秒后行人按钮)
✅ 必要时添加守卫([行人请求])
✅ 自转换用于重复动作
✅ 入口/出口动作用于行为
✅ 清晰的布局,易读的字体


🎯 最后的话:你已经准备好了!

你刚刚学会了:

  • 什么是状态机图

  • 如何以状态和事件的思维来思考

  • 如何绘制阅读像专业人士一样

  • 如何建模真实系统,例如交通灯

  • 如何使用 PlantUML编写清晰、可维护的图表

🎉 你不仅仅在学习UML——你正在学习如何建模真实系统一次一个状态。


📌 下一步(你的学习路径)

  1. 手绘三状态交通灯——无需工具,只需纸笔。

  2. 尝试使用PlantUML使用上面的代码——查看其渲染效果。

  3. 修改:更改等待时间。添加“紧急覆盖”状态。

  4. 尝试使用自动售货机→ 相同逻辑,但涉及金钱。

  5. 绘制你自己的:一个游戏角色(行走 → 跳跃 → 攻击 → 死亡)。

💬 需要帮助吗?试试这个:“我正试图建模一个[你的系统]——你能帮我创建一个状态机吗?”


🙌 最后思考

🔄 任何会变化的事物——无论是灯、登录还是订单——都可以用状态机来建模。
你不需要是程序员也能理解它。你只需要问:“这个事物可能处于什么状态,是什么让它发生变化?”


✅ 你现在知道如何创建专业且可用的状态机图表——从初学者成长为自信的建模者。

🎉 祝你绘图愉快!
如果需要可打印的PDF版本、测验或编程挑战来检验你的技能,请告诉我。


Leave a Reply