【设计模式】有限状态机(FSM)学习笔记


有限状态机(FSM)学习笔记

1. 定义与定位

有限状态机是一种管理对象状态变化的行为模型,也是一种经典的设计模式。 它将物理世界的连续变化抽象为逻辑上的有限个状态,并定义状态之间转换的规则。

  • 核心价值:简化复杂逻辑,使系统行为清晰可控。
  • 适用场景:运动控制、工艺流程管理、游戏AI、UI界面逻辑等。

2. 核心概念与运行流

状态机的本质

FSM 将连续的物理量离散化。例如风机转速可能是 1000rpm 或 1400rpm,但在 FSM 的逻辑世界里,我们只关注它是处于“高速档”还是“低速档”、“启动”还是“停止”。

运行流(生命周期)

状态机的运行遵循以下路径:

现态 \rightarrow 监听事件 \rightarrow 满足条件 \rightarrow 执行动作 \rightarrow 次态

3. 状态机的四要素

状态机的逻辑构建主要依赖于四个核心要素。“现态”和“条件”是因,“动作”和“次态”是果。

要素别称说明
现态当前状态对象当前所处的逻辑状态(如:Idle 空闲状态)。
条件事件/输入触发状态迁移的诱因。当事件发生且满足监护条件时,触发迁移。
动作输出/行为条件满足后执行的操作。注:动作不是必须的,且执行位置影响状态机类型(见下文)。
次态目标状态迁移的目的地。一旦激活,它即转变为新的“现态”。

4. 进阶理解:Moore 型与 Mealy 型

根据“动作”执行时机的不同,状态机可分为两类,这对代码实现有直接影响:

  • Mealy 型(米利机)
    • 动作依附于“迁移”
    • 逻辑:输入 -> 动作 -> 状态改变
    • 特点:响应速度快,动作只在转换瞬间执行一次。
  • Moore 型(摩尔机)
    • 动作依附于“状态”
    • 逻辑:状态改变 -> 进入状态 -> 持续执行动作
    • 特点:输出仅与状态有关,逻辑更稳定,但响应比 Mealy 型慢一拍。

5. 状态迁移图(STD)

STD 是状态机思想的可视化体现,主要包含抽象化与状态转移两层含义。参考 UML 规范,STD的绘制方法如下:

1784529654296

  1. 状态框:圆角矩形表示,框内注明状态名(如 Running)。

  2. 初始伪状态:实心黑色圆点(●),表示状态机的起点。

  3. 终止伪状态:圆圈内套圆圈(◉),表示状态机的终点。

  4. 转换箭头:带箭头的直线,表示状态迁移方向。

  5. 转换标签:写在箭头上的文本,格式为 事件 [监护条件] / 动作

    • 示例:按键按下 [电压正常] / 启动电机
    • 注:不要混淆流程图菱形框,状态图的条件通常直接标注在箭头上。

6. 基于FSM的红绿灯控制

本章节将通过一个具体的红绿灯控制系统,演示如何将 FSM 的理论概念落地为代码。该案例不仅实现了基本的逻辑控制,还展示了优秀的软件工程分层架构。

6.1 系统分析与状态定义

红绿灯系统是典型的 Moore 型状态机(输出仅依赖于当前状态),其核心在于将“时间控制”与“状态切换”分离。

  • 状态
    • IDLE: 初始空闲状态。
    • RED: 红灯亮起。
    • GREEN: 绿灯亮起。
    • YELLOW: 黄灯亮起。
  • 事件/条件:计时器超时、用户手动输入指令。
  • 动作:输出灯色信号、显示倒计时。
  • 转换规则
    • RED \rightarrow GREEN
    • GREEN \rightarrow YELLOW
    • YELLOW \rightarrow RED
    • IDLE \rightarrow RED/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 解耦

所有的 printinput 都独立在第四层。

  • 依赖倒置:展示函数(如 show_countdown)只接受它需要的参数(状态枚举、时长),而不依赖整个控制器类或数据字典结构。
  • 易于替换:未来如果要将控制台程序改为 GUI 或 Web 界面,只需修改此层,引擎和业务逻辑层完全复用。

6.4 关键设计思想总结

设计原则具体体现价值
配置优于代码使用IntEnum 和字典定义状态与规则修改业务逻辑无需改动引擎代码,无需重新编译。
单一职责 (SRP)状态机只管状态转换,控制器管计时,UI管显示代码逻辑清晰,bug 定位精准。
组合优于继承Controller 持有 StateMachine封装性好,防止外部非法调用内部方法。
显式优于隐式转换规则集中在表中,而非分散在if-else规则一目了然,易于审计和查错。

6.5 状态迁移图 (STD)

根据上述逻辑,红绿灯系统的状态迁移图如下(参考第 5 节符号规范):

  • 初始伪状态 (●) → IDLE
  • IDLE --[指令/自动]--> RED
  • RED --[超时/5s]--> GREEN
  • GREEN --[超时/4s]--> YELLOW
  • YELLOW --[超时/2s]--> RED

加载评论中…

发表评论