3、FSM建模方法:从需求到状态机的建模步骤
有限状态机(FSM)建模的核心价值在于将模糊、动态的系统行为转化为精确、可验证的离散逻辑。然而,从一段自然语言描述的需求到一张可落地的状态图,中间存在显著的认知鸿沟。许多工程师习惯于直接绘制状态图,导致遗漏关键事件、混淆状态与动作、产生不可达状态或状态爆炸。本章将系统阐述从需求文本到状态机模型的完整工程化路径,涵盖状态识别、事件提取、转移条件定义以及状态图绘制规范,并通过一个完整的案例闭环展示每一步的具体操作。
3.1 核心原理:状态机建模的本质是“离散化”
FSM建模的本质是将连续变化的系统行为在时间轴上离散化。系统在任何时刻只能处于有限个状态中的一个,状态之间的切换由事件驱动,并受转移条件约束。这一离散化过程要求建模者回答三个核心问题:系统在哪些时刻是“稳定”的?什么外部或内部刺激会打破这种稳定?打破后系统如何响应并进入下一个稳定态?
从需求到状态机的映射并非一一对应。需求文本中的“功能描述”往往混合了状态、事件、动作和约束条件。例如,“当用户点击支付按钮后,系统校验余额,若余额充足则扣款并跳转到成功页,否则提示余额不足”这段描述中:“待支付”是状态,“点击支付”是事件,“余额充足”是条件,“扣款”是动作,“成功页”是另一个状态。建模者的核心技能在于从自然语言中准确剥离这些不同性质的元素。
3.2 判断方法与操作流程:五步建模法
以下五步法将建模过程标准化,每一步都有明确的输入、操作和输出,可有效降低遗漏和歧义。
| 步骤 | 输入 | 核心操作 | 输出 | 质量检查点 |
|---|---|---|---|---|
| ① 需求分析 | 需求文档/用户故事 | 划分子系统、识别业务场景 | 场景列表 | 每个场景有明确的触发和结束 |
| ② 状态识别 | 场景列表 | 找出所有“稳定等待期”,命名状态 | 状态清单 | 状态互斥、穷尽、可命名 |
| ③ 事件提取 | 需求文本+状态清单 | 找出所有触发状态变化的动词短语 | 事件列表 | 每个事件至少连接两个状态 |
| ④ 转移定义 | 状态+事件 | 为每个事件指定源状态、目标状态、条件、动作 | 转移表 | 无重复、无冲突、无死锁 |
| ⑤ 绘制校验 | 转移表 | 绘制状态图,进行可达性/完备性检查 | 状态图+校验报告 | 所有状态可达,所有事件有响应 |
3.2.1 状态识别的具体技巧
识别状态时,建议在需求文本中圈出所有名词性短语,尤其是那些描述系统“处于某种情况”的词汇。例如:“订单已创建”“支付处理中”“退款审核中”“交易关闭”。这些短语通常对应状态。另一个技巧是寻找“等待”语义——系统在等待用户输入、等待外部回调、等待定时器超时,这些等待点就是状态的候选。需要特别注意的是,状态必须具有“持久性”,即系统在该状态下会停留一段时间,而不是瞬间完成。例如“正在扣款”如果是一个原子操作(几十毫秒),则不应建模为状态,而应建模为转移动作。
3.2.2 事件与条件的区分
事件是已经发生的、瞬时的事情,如“用户按下按钮”“收到网络响应”“定时器溢出”。条件是事件发生时系统需要检查的布尔表达式,如“余额 > 0”“重试次数 < 3”。一个常见的建模错误是将条件当作事件。例如,“当余额不足时”不是事件,而是条件;真正的事件是“用户请求支付”,条件为“余额不足”。在状态图中,事件写在斜杠左侧,条件写在方括号内,动作写在斜杠右侧:事件[条件]/动作。
3.3 案例拆解:在线支付订单状态机建模
为完整展示五步法的应用,我们以一个典型的电商在线支付订单为例。该案例包含真实的业务约束和异常分支,适合演示建模全过程。
3.3.1 步骤①:需求分析与场景划分
该需求包含三个主要业务场景:正常支付流程(待支付→已支付)、超时/取消流程(待支付→已关闭)、退款流程(已支付→已退款)。每个场景都有明确的触发事件和终态。
3.3.2 步骤②:状态识别
从需求中提取名词性稳定期:待支付、已支付、已关闭、已退款。此外,退款审核过程是一个典型的中间状态,需求中隐含了“退款审核中”状态——因为审核需要时间,系统必须等待审核结果。因此状态清单为:待支付、已支付、退款审核中、已退款、已关闭。共5个状态。
3.3.3 步骤③:事件提取
提取动词短语:提交订单(进入初始状态)、点击去支付、支付成功回调、用户取消、30分钟超时、申请退款、审核通过、审核不通过、退款完成。其中“提交订单”是进入初始状态的动作,不是状态间转移事件。“退款完成”是审核通过后的后续动作,可以合并到审核通过事件链中。
3.3.4 步骤④:转移定义
构建转移表,明确每个事件的源状态、目标状态、条件和动作:
| 源状态 | 事件 | 条件 | 动作 | 目标状态 |
|---|---|---|---|---|
| 待支付 | 点击去支付 | 支付单创建成功 | 跳转收银台 | 待支付(自转移) |
| 待支付 | 支付成功回调 | 签名验证通过 | 更新订单状态 | 已支付 |
| 待支付 | 用户取消 | 无 | 释放库存 | 已关闭 |
| 待支付 | 30分钟超时 | 无 | 系统自动关闭 | 已关闭 |
| 已支付 | 申请退款 | 订单在售后期内 | 创建退款单 | 退款审核中 |
| 退款审核中 | 审核通过 | 无 | 执行退款 | 已退款 |
| 退款审核中 | 审核不通过 | 无 | 通知用户 | 已支付 |
注意“点击去支付”是一个自转移,因为用户可能多次点击或支付单创建失败后重试。该转移不改变状态,但执行了动作(创建支付单)。
3.3.5 步骤⑤:绘制与校验
根据转移表绘制状态图后,进行三项校验:可达性(所有状态都能从初始状态到达)、完备性(每个状态下的每个可能事件都有明确响应)、一致性(没有冲突的转移)。本例中,若“已关闭”状态收到“支付成功回调”事件,需求未定义——这属于异常流,需要补充处理策略(如:关闭后支付回调,应触发退款流程)。
3.4 状态图绘制规范与常见误区
状态图绘制遵循UML标准,但工程实践中常因不规范导致沟通成本上升。以下是必须遵守的规范:
- 初始状态:实心圆点,一个状态图有且仅有一个。
- 终止状态:实心圆点外加圆圈,可以有多个或没有(如系统常驻状态机)。
- 状态框:圆角矩形,状态名称居中,可包含内部动作(entry/exit/do)。
- 转移箭头:实线箭头,标签格式为
事件[条件]/动作,三者均可选,但至少有一个。 - 选择伪状态:菱形,用于多条件分支,避免箭头交叉。
- 避免“转移地狱”:当状态超过10个或转移超过30条时,应考虑使用层次状态机(HSM)或状态模式重构。
常见的建模误区包括:将动作建模为状态(如“正在扣款”)、遗漏超时事件、混淆事件与条件、忽略自转移、未定义初始状态等。通过严格执行五步法,并配合转移表进行完整性检查,可规避绝大多数问题。最终交付的状态图应能回答:系统在任何时刻处于哪个状态?什么事件会触发转移?转移需要满足什么条件?转移时执行什么动作?