--- tags: [test/review, fsm, pattern, finite-state-machine] create time: 2026-08-09 12:00 --- # 有限状态机核心概念 — 测试题 ## 概述 本测试覆盖 FSM 四大核心概念(State/Event/Transition/Action)、DFA vs NFA、状态转移方程以及三种工程实现方式(switch-case/map-of-function/State接口)。共 10 道题目(6 选择 + 3 填空 + 1 简答)。 --- ## 一、选择题(6道,由浅入深) > **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景 ### Q1(基础)— 考察定义层面 FSM 的四元组模型 `(States, Events, Transitions, Actions)` 中缺少了哪个原文提到的第五元素? A. StartState(初始状态) B. EndState(终止状态) C. CurrentState(当前状态) D. DefaultAction(默认动作) ### Q2(基础)→ 以下关于 DFA 和 NFA 的描述哪个是正确的? A. DFA 允许一个状态对同一事件转移到多个后继状态 B. NFA 的行为可预测,适合工程实现 C. 工程实践中的 FSM 几乎总是 DFA D. NFA 比 DFA 表达能力更强 ### Q3(进阶)— 核心原理 关于 FSM 的状态转移函数 δ(current_state, event) → next_state,以下说法哪个是正确的? A. 转移函数必须是全函数——所有事件在所有状态下都必须有定义 B. 转移函数必须是部分函数——并非所有事件在所有状态下都有效 C. 非法转移应该静默忽略而非报错 D. 转移函数可以返回多个后继状态以支持并行执行 ### Q4(进阶)— 比较/辨析 在 Go 中实现 FSM 时,如果状态数少于 10 且逻辑简单,最推荐的实现方式是: A. State 接口模式 B. map-of-function-map C. switch-case 枚举 D. 手写一个完整的 FSM 框架库 ### Q5(深入)— 场景推理 某系统有 60 个不同的业务状态,每个状态的转换行为复杂且涉及大量副作用(发送邮件、写审计日志、调用外部 API)。以下哪种 FSM 实现方式最适合? A. switch-case 枚举(60+ case 分支) B. map-of-function-map(闭包传递上下文繁琐) C. State 接口 + Context 模式(单一职责,面向对象) D. if-else 链 ### Q6(深入)— 源码级/边界场景 原文的警告中提到:"常见误区是_____。"这个误区具体指什么? A. 用数字枚举做状态校验 B. 把 FSM 当成普通的 if-else C. 忘记设置初始状态 D. 没有为每个状态实现 Enter 方法 --- ## 二、填空题(3道) ### F1 — 填空1 FSM 的五元组是:`(States, Events, Transitions, Actions, _____)`。当转移存在 Action 时,转移函数扩展为 `δ(current_state, event) → (next_state, _____)`。 > **提示**: 回忆五元组和扩展转移函数的原句。 ### F2 — 填空2 面试常考点:不建议用数字枚举 + 硬编码数组来做状态校验,因为数字枚举不具备_____表达能力,编译期无法捕获非法转移。 > **提示**: 形容词,表示状态名称能传达的信息含义。 ### F3 — 填空3 Switch-case 方案适用状态数 ≤ _____;map-of-function 适用 _____~50;State 接口适用 > _____。这是一个经验分界线。 > **提示**: 三个空填数值(两个可能相同)。 --- ## 三、简答题(1道) ### S1 面试官问:"请解释为什么 FSM 的核心价值不在于'减少代码行数',而在于'把所有合法转换集中在一处定义'。结合具体例子说明散落在各处的好处检查与集中管理相比有哪些问题。" > **答题框架提示**: > 1. "分散判断"的典型场景和问题 > 2. "集中定义"的优势 > 3. 维护成本对比 > 4. 测试便利性 --- ## 参考答案与解析 ### 选择题答案 | 题号 | 正确答案 | 解析 | |------|---------|------| | Q1 | A | 原文提到五元组是 `(States, Events, Transitions, Actions, StartState)`。StartState 定义了 FSM 的起始位置,没有它 FSM 就不知道从哪里开始。EndState 不是必需的——有些 FSM 是循环的。 | | Q2 | C | A 错:DFA 是确定性的,每个事件最多一个后继;B 错:NFA 非确定性不可预测;D 错:两者等价但 NFA 更紧凑。只有 C 正确——工程实践中几乎总是 DFA。 | | Q3 | B | 转移函数是**部分函数**——并非所有事件在所有状态下都有定义。例如订单已退款后不能再支付。非法转移应返回错误或拒绝,而非静默忽略。 | | Q4 | C | 状态少(≤10)逻辑简单的场景用最直观的 switch-case。不选 B 是因为 map-of-function 在状态少时过度设计,不选 A 是因为开关太多违反可读性。 | | Q5 | C | 60 个状态超过 switch-case 和 map 的舒适区。State 接口的优势是每个状态独立封装行为(单一职责),适合状态多且行为复杂的场景。 | | Q6 | B | 原文原文警告:常见误区是把 FSM 当成普通的 if-else。FSM 的核心价值是把所有合法转换**集中在一处定义**,而不是散落在业务逻辑各处。 | ### 填空题答案 | 题号 | 答案 | 解析 | |------|------|------| | F1 | `StartState`;`action` | 标准五元组加上初始状态定义了 FSM 的起点。扩展转移函数同时输出下一个状态和执行的动作(如发邮件、写日志)。 | | F2 | `语义`(或"有意义的") | 数字枚举(如 0=PENDING, 1=PAID)本身不具备可读性和语义表达力,编译器也无法在编译期验证某个转移是否合法。字符串枚举或 map 方式更好。 | | F3 | `10`;`10`;`50` | switch-case ≤10;map-of-function 10~50;State 接口 >50。这是按规模和复杂度递增的工程选型指南。 | ### 简答题参考答案 S1:**参考答案要点**: 1. **分散判断的问题**:在 if-else 风格的代码中,状态转换规则往往嵌套在每个业务方法里(如 `if order.status == "paid" && event == "ship"`)。新开发者需要逐个方法查找才能了解完整转换图,新增转换要在多处修改,极易遗漏。 2. **集中管理的优势**:将所有合法的 `state -> event -> next_state` 映射集中到一个数据结构(map 或表格)中,一目了然。新增转换只需注册一条规则。 3. **维护成本**:分散模式下改一个转换可能需要找遍整个代码库;集中模式下只需更新一张表。bug 更容易定位和修复。 4. **测试便利性**:集中定义可以生成完整的转换矩阵用于测试——遍历所有 state-event 组合确保要么通过要么报明确的错误。分散模式下很难做到全覆盖测试。 **评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。 ## 关联笔记 - [[有限状态机在业务中的应用]]