【设计模式】有限状态机(FSM)学习笔记
有限状态机(FSM)学习笔记
1. 定义与定位
有限状态机是一种管理对象状态变化的行为模型,也是一种经典的设计模式。 它将物理世界的连续变化抽象为逻辑上的有限个状态,并定义状态之间转换的规则。
- 核心价值:简化复杂逻辑,使系统行为清晰可控。
- 适用场景:运动控制、工艺流程管理、游戏AI、UI界面逻辑等。
2. 核心概念与运行流
状态机的本质
FSM 将连续的物理量离散化。例如风机转速可能是 1000rpm 或 1400rpm,但在 FSM 的逻辑世界里,我们只关注它是处于“高速档”还是“低速档”、“启动”还是“停止”。
运行流(生命周期)
状态机的运行遵循以下路径:
现态 监听事件 满足条件 执行动作 次态
3. 状态机的四要素
状态机的逻辑构建主要依赖于四个核心要素。“现态”和“条件”是因,“动作”和“次态”是果。
| 要素 | 别称 | 说明 |
|---|---|---|
| 现态 | 当前状态 | 对象当前所处的逻辑状态(如:Idle 空闲状态)。 |
| 条件 | 事件/输入 | 触发状态迁移的诱因。当事件发生且满足监护条件时,触发迁移。 |
| 动作 | 输出/行为 | 条件满足后执行的操作。注:动作不是必须的,且执行位置影响状态机类型(见下文)。 |
| 次态 | 目标状态 | 迁移的目的地。一旦激活,它即转变为新的“现态”。 |
4. 进阶理解:Moore 型与 Mealy 型
根据“动作”执行时机的不同,状态机可分为两类,这对代码实现有直接影响:
- Mealy 型(米利机):
- 动作依附于“迁移”。
- 逻辑:
输入 -> 动作 -> 状态改变。 - 特点:响应速度快,动作只在转换瞬间执行一次。
- Moore 型(摩尔机):
- 动作依附于“状态”。
- 逻辑:
状态改变 -> 进入状态 -> 持续执行动作。 - 特点:输出仅与状态有关,逻辑更稳定,但响应比 Mealy 型慢一拍。
5. 状态迁移图(STD)
STD 是状态机思想的可视化体现,主要包含抽象化与状态转移两层含义。参考 UML 规范,STD的绘制方法如下:

-
状态框:圆角矩形表示,框内注明状态名(如
Running)。 -
初始伪状态:实心黑色圆点(●),表示状态机的起点。
-
终止伪状态:圆圈内套圆圈(◉),表示状态机的终点。
-
转换箭头:带箭头的直线,表示状态迁移方向。
-
转换标签:写在箭头上的文本,格式为
事件 [监护条件] / 动作。- 示例:
按键按下 [电压正常] / 启动电机 - 注:不要混淆流程图菱形框,状态图的条件通常直接标注在箭头上。
- 示例:
6. 基于FSM的红绿灯控制
本章节将通过一个具体的红绿灯控制系统,演示如何将 FSM 的理论概念落地为代码。该案例不仅实现了基本的逻辑控制,还展示了优秀的软件工程分层架构。
6.1 系统分析与状态定义
红绿灯系统是典型的 Moore 型状态机(输出仅依赖于当前状态),其核心在于将“时间控制”与“状态切换”分离。
- 状态:
IDLE: 初始空闲状态。RED: 红灯亮起。GREEN: 绿灯亮起。YELLOW: 黄灯亮起。
- 事件/条件:计时器超时、用户手动输入指令。
- 动作:输出灯色信号、显示倒计时。
- 转换规则:
REDGREENGREENYELLOWYELLOWREDIDLERED/GREEN/YELLOW(初始化)
6.2 实现架构:分层设计
为了避免代码耦合(即数据、规则、行为混杂在一起),本系统采用 五层架构 设计,每一层只依赖其下一层,实现高内聚低耦合。
┌──────────────────────────────────────────────┐
│ 第五层:main() │ ← 程序入口
├──────────────────────────────────────────────┤
│ 第四层:run_automatic() / run_manual() │ ← 展示/交互层 (I/O)
├──────────────────────────────────────────────┤
│ 第三层:TrafficLightController │ ← 业务逻辑层 (计时+规则)
├──────────────────────────────────────────────┤
│ 第二层:StateMachine │ ← 引擎层 (通用状态机)
├──────────────────────────────────────────────┤
│ 第一层:LightState / STATE_TRANSITIONS │ ← 数据层 (纯配置)
└──────────────────────────────────────────────┘
6.3 核心代码逻辑解析
1. 数据层:枚举与配置表
使用 IntEnum 代替裸整数(魔法数字),利用字典集中管理转换规则,实现 配置与逻辑分离。
- 优势:IDE 自动补全、防止拼写错误、易于重构。
- 集中式管理:所有转换规则在
STATE_TRANSITIONS一张表中可见,避免遗漏。
from enum import IntEnum
class LightState(IntEnum):
IDLE = 0x00
RED = 0x01
GREEN = 0x02
YELLOW = 0x03
# 集中声明转换规则(邻接表)
STATE_TRANSITIONS = {
LightState.IDLE: [LightState.RED, LightState.GREEN, LightState.YELLOW],
LightState.RED: [LightState.GREEN],
LightState.GREEN: [LightState.YELLOW],
LightState.YELLOW: [LightState.RED],
}
# 状态持续时间配置
STATE_DURATIONS = {
LightState.RED: 5.0,
LightState.GREEN: 4.0,
LightState.YELLOW: 2.0,
}
2. 引擎层:通用状态机
引擎层是一个通用的 StateMachine 类,它不知道“交通灯”的存在,只处理抽象的“状态”和“转换”。
- 通用性:同样的引擎代码可以直接用于烤箱、电机等其他 FSM 场景,无需修改。
- 职责:校验转换合法性、执行切换。
class StateMachine:
def __init__(self, initial_state, transitions):
self._current = initial_state
self._transitions = transitions
def transition_to(self, target):
if not self.can_transition_to(target):
return False
self._current = target
return True
3. 业务逻辑层:控制器
TrafficLightController 通过 组合 而非继承的方式持有 StateMachine 实例。
- 组合优于继承:继承会暴露内部状态机的所有方法(如强制切换),而组合将状态机封装为私有成员,外部只能通过
controller.step()进行受控操作。 - 业务封装:负责处理计时逻辑(“何时切换”)和循环次数管理。
class TrafficLightController:
def __init__(self, initial_state, total_cycles):
# 组合:持有状态机实例
self._fsm = StateMachine(initial_state, STATE_TRANSITIONS)
self.total_cycles = total_cycles
def step(self, target):
"""执行一步状态切换,包含业务校验"""
if target not in (LightState.RED, ...):
return (False, "未知状态")
if not self._fsm.transition_to(target):
return (False, "不允许的转换")
return (True, None)
4. 交互层:I/O 解耦
所有的 print 和 input 都独立在第四层。
- 依赖倒置:展示函数(如
show_countdown)只接受它需要的参数(状态枚举、时长),而不依赖整个控制器类或数据字典结构。 - 易于替换:未来如果要将控制台程序改为 GUI 或 Web 界面,只需修改此层,引擎和业务逻辑层完全复用。
6.4 关键设计思想总结
| 设计原则 | 具体体现 | 价值 |
|---|---|---|
| 配置优于代码 | 使用IntEnum 和字典定义状态与规则 | 修改业务逻辑无需改动引擎代码,无需重新编译。 |
| 单一职责 (SRP) | 状态机只管状态转换,控制器管计时,UI管显示 | 代码逻辑清晰,bug 定位精准。 |
| 组合优于继承 | Controller 持有 StateMachine | 封装性好,防止外部非法调用内部方法。 |
| 显式优于隐式 | 转换规则集中在表中,而非分散在if-else | 规则一目了然,易于审计和查错。 |
6.5 状态迁移图 (STD)
根据上述逻辑,红绿灯系统的状态迁移图如下(参考第 5 节符号规范):
- 初始伪状态 (●) →
IDLE IDLE--[指令/自动]-->REDRED--[超时/5s]-->GREENGREEN--[超时/4s]-->YELLOWYELLOW--[超时/2s]-->RED