资讯动态

状态机(FSM)核心概念与实战:从自动售货机到复杂系统设计

发布时间:2026/8/23 7:28:45 来源:尧图企业网站定制
1. 状态机从“自动售货机”到复杂系统的思维模型如果你写过代码尤其是处理过用户交互、订单流转或者设备控制那你大概率已经和状态机打过交道了只是可能没意识到。我第一次真正理解状态机是在调试一个嵌入式设备的按键逻辑时。当时一个简单的“短按开机、长按关机”功能被我写成了一团乱麻的if-else嵌套各种边界条件比如按键抖动、中途松手让我焦头烂额。直到我把整个流程画成几个“状态”和它们之间的“转换”代码瞬间清晰了。状态机不是某个特定语言或框架的专利它是一种思维方式一种将复杂、离散的事件驱动行为规整成清晰、可预测模型的方法。简单说它回答了一个核心问题“在什么情况下从哪来到哪去去干什么”今天我们就抛开那些晦涩的学术定义从一个自动售货机开始手把手拆解状态机的核心概念用图示和代码让你直观感受再深入到嵌入式、后端、前端乃至硬件设计中的实战场景。你会发现无论是Verilog里控制流水线的有限状态机FSM还是Spring中管理订单生命周期的StateMachine或是游戏里角色的行为树底层其灵魂都是相通的。理解了这个模型你就多了一种化繁为简、让代码更健壮的设计武器。2. 核心概念拆解状态、事件、转换与动作要玩转状态机必须先吃透它的四个基本构件状态State、事件Event、转换Transition和动作Action。我们用一个最经典的例子——自动售货机——来具象化它们。2.1 状态系统在某个时刻的“快照”状态是系统可能处于的、相对稳定的情形。对于一台售货机它不可能同时既在“等待投币”又在“出货”。在任一时刻它必定处于以下几个状态之一空闲Idle等待用户投币。已投币CoinInserted用户投入了足额或部分金额。选择商品ItemSelected用户按下了商品按钮。出货Dispensing机器正在吐出商品。找零ReturningChange机器正在退还零钱。注意定义状态的关键是“互斥”和“完备”。互斥意味着同一时间只能在一个状态完备意味着所有可能的情况都应被某个状态覆盖。在设计时我常会自问“系统现在‘正在做什么’或‘等待什么’” 答案往往就是一个状态名。2.2 事件触发状态改变的“导火索”事件是来自外部或内部、促使系统可能发生改变的信号。它不是状态而是瞬间发生的。对售货机来说投币coinInserted选择商品itemSelected出货完成dispenseCompleted找零完成changeReturned取消cancel2.3 转换状态因事件而改变的过程转换定义了在某个状态下当特定事件发生时系统将迁移到哪个新状态。这是状态机的核心逻辑。例如在空闲Idle状态下发生投币coinInserted事件转换到已投币CoinInserted状态。在已投币CoinInserted状态下发生选择商品itemSelected事件转换到出货Dispensing状态。2.4 动作转换发生时或处于状态时执行的操作动作是转换或状态激活时执行的具体行为。它可以关联在转换上转换动作也可以关联在进入/退出某个状态时进入/退出动作。转换动作从“已投币”转换到“出货”时执行“扣减库存”、“驱动电机”动作。进入动作进入“出货”状态时执行“点亮出货指示灯”。退出动作退出“空闲”状态时执行“关闭待机屏幕”。把这四要素组合起来就构成了状态机的完整行为描述。一个健壮的状态机设计必须明确每一个状态下对所有可能事件的响应。对于未定义的事件比如在“出货”状态收到“投币”事件通常有两种处理要么忽略要么触发一个错误处理流程。3. 可视化理解两种核心状态机图示图比文字更直观。状态机主要有两种图形化表示方法状态转移图和状态转移表。它们互为补充前者适合设计阶段沟通后者适合实现阶段查漏。3.1 状态转移图一目了然的全景流程状态转移图用圆圈或圆角矩形表示状态用带箭头的线表示转换线上标注触发事件和可选的动作。下图描绘了一个简化版的自动售货机状态流[空闲] --投币-- [已投币] [已投币] --选择商品-- [出货] [已投币] --取消-- [找零] --找零完成-- [空闲] [出货] --出货完成-- [找零] 注此为文字描述实际应为图形。[状态] --事件/动作-- [新状态]看图的关键点起点通常有一个初始状态如“空闲”可能用一条来自黑点的箭头指向它。终点可能有终止状态本例中售货机循环运行无绝对终点。自循环一个状态可能因某个事件转换到自身。例如“已投币”状态下再次“投币”可能只是增加金额状态不变但会执行“更新金额显示”的动作。条件分支同一个状态同一个事件可能根据不同的条件守卫条件转换到不同的新状态。例如“已投币”状态下“选择商品”如果金额足够则转到“出货”如果金额不足则可能转到“金额不足提示”状态。在团队协作中画图是统一认知、避免歧义最有效的方式。我习惯用PlantUML或draw.io来画甚至在白板上画草图讨论定稿后再编码。3.2 状态转移表严谨无遗漏的逻辑矩阵当状态和事件较多时图可能变得杂乱。状态转移表则以矩阵形式清晰列出每个“状态-事件”对对应的“下一状态”和“执行动作”。它强迫你思考每一个组合避免遗漏。以下是一个简化的状态转移表示例当前状态事件守卫条件下一状态执行动作空闲 (Idle)投币无已投币 (CoinInserted)显示投入金额已投币 (CoinInserted)选择商品金额 商品价格出货 (Dispensing)扣库存启动电机已投币 (CoinInserted)选择商品金额 商品价格已投币 (CoinInserted)显示金额不足已投币 (CoinInserted)取消无找零 (ReturningChange)计算应退金额出货 (Dispensing)出货完成无找零 (ReturningChange)停止电机找零 (ReturningChange)找零完成无空闲 (Idle)清零显示复位实操心得在实现复杂状态机前我强制自己先填完这个表。这能暴露出很多边缘情况比如“出货过程中断电怎么办”可能需要一个“故障”状态。表格也是编写单元测试用例的绝佳输入你可以针对每一行设计测试。4. 从概念到代码三种经典实现模式理解了概念和图示我们来看代码如何落地。状态机的实现模式多样从最直接的switch-case到面向对象的状态模式再到使用专用框架。这里介绍三种最典型、跨语言通用的模式。4.1 模式一枚举switch-case简单直接这是最朴素、最容易理解的方式适用于状态数量有限、逻辑简单的场景。from enum import Enum class VendingMachineState(Enum): IDLE 1 COIN_INSERTED 2 DISPENSING 3 RETURNING_CHANGE 4 class VendingMachine: def __init__(self): self.current_state VendingMachineState.IDLE self.balance 0 def handle_event(self, event, **kwargs): if self.current_state VendingMachineState.IDLE: if event coin_inserted: amount kwargs.get(amount, 0) self.balance amount print(f投入{amount}元当前余额{self.balance}元) self.current_state VendingMachineState.COIN_INSERTED else: print(f状态{self.current_state}下忽略事件{event}) elif self.current_state VendingMachineState.COIN_INSERTED: if event item_selected: price kwargs.get(price, 0) if self.balance price: print(f商品价格{price}元开始出货) self.balance - price self.current_state VendingMachineState.DISPENSING # 异步触发出货完成事件 self.handle_event(dispense_completed) else: print(f余额不足还需{price - self.balance}元) elif event cancel: print(取消交易准备找零) self.current_state VendingMachineState.RETURNING_CHANGE else: print(f状态{self.current_state}下忽略事件{event}) # ... 其他状态的处理 elif self.current_state VendingMachineState.DISPENSING: if event dispense_completed: print(出货完成) if self.balance 0: self.current_state VendingMachineState.RETURNING_CHANGE else: self.current_state VendingMachineState.IDLE elif self.current_state VendingMachineState.RETURNING_CHANGE: if event change_returned: print(f找零{self.balance}元) self.balance 0 self.current_state VendingMachineState.IDLE # 使用示例 vm VendingMachine() vm.handle_event(coin_inserted, amount5) vm.handle_event(item_selected, price3)优点直白无需引入额外复杂度适合快速原型或简单逻辑。缺点所有逻辑堆在一个巨型switch或if-elif链中难以维护。新增状态或事件需要修改中心处理器违反开闭原则。4.2 模式二状态模式面向对象易于扩展这是GoF设计模式中的“状态模式”。它为每个状态定义一个类并将状态转换的职责委托给这些状态对象。from abc import ABC, abstractmethod class State(ABC): 状态接口 abstractmethod def insert_coin(self, machine, amount): pass abstractmethod def select_item(self, machine, price): pass abstractmethod def cancel(self, machine): pass abstractmethod def dispense(self, machine): pass class IdleState(State): def insert_coin(self, machine, amount): machine.balance amount print(f投入{amount}元当前余额{machine.balance}元) machine.set_state(CoinInsertedState()) def select_item(self, machine, price): print(请先投币) def cancel(self, machine): print(未投币无需取消) def dispense(self, machine): print(未购买商品无法出货) class CoinInsertedState(State): def insert_coin(self, machine, amount): machine.balance amount print(f追加投入{amount}元当前余额{machine.balance}元) def select_item(self, machine, price): if machine.balance price: machine.balance - price print(f购买成功扣款{price}元开始出货) machine.set_state(DispensingState()) machine.dispense() # 触发出货动作 else: print(f余额不足还需{price - machine.balance}元) def cancel(self, machine): print(交易取消准备找零) machine.set_state(ReturningChangeState()) def dispense(self, machine): print(请先选择商品) class DispensingState(State): def insert_coin(self, machine, amount): print(出货中请稍候) def select_item(self, machine, price): print(出货中请稍候) def cancel(self, machine): print(出货中无法取消) def dispense(self, machine): # 模拟出货耗时操作 print(...出货完成) if machine.balance 0: machine.set_state(ReturningChangeState()) else: machine.set_state(IdleState()) class ReturningChangeState(State): def insert_coin(self, machine, amount): print(找零中请稍候) def select_item(self, machine, price): print(找零中请稍候) def cancel(self, machine): print(找零中无法取消) def dispense(self, machine): print(找零中无法出货) def return_change(self, machine): # 状态特有的方法 print(f找零{machine.balance}元) machine.balance 0 machine.set_state(IdleState()) class VendingMachine: def __init__(self): self.balance 0 self._state IdleState() # 持有当前状态对象的引用 def set_state(self, state): self._state state def insert_coin(self, amount): self._state.insert_coin(self, amount) def select_item(self, price): self._state.select_item(self, price) def cancel(self): self._state.cancel(self) def dispense(self): self._state.dispense(self) def return_change(self): if isinstance(self._state, ReturningChangeState): self._state.return_change(self) # 使用示例 vm VendingMachine() vm.insert_coin(5) vm.select_item(3) # 内部状态自动流转优点符合开闭原则新增状态只需添加新类无需修改其他状态类或上下文。状态逻辑分散到各个类中结构清晰。缺点类数量会随着状态数增长而增长。状态之间如果相互引用较多可能会稍显复杂。4.3 模式三状态转移表驱动配置化高度灵活将状态转移表见3.2节直接映射到代码数据结构中。核心是一个查找表根据当前状态和事件查找并执行对应的转换和动作。# 定义状态和事件枚举 State Enum(State, IDLE COIN_INSERTED DISPENSING RETURNING_CHANGE) Event Enum(Event, COIN_INSERTED ITEM_SELECTED CANCEL DISPENSE_COMPLETED CHANGE_RETURNED) # 定义动作函数 def action_show_balance(machine, amount): print(f显示余额: {machine.balance}) def action_deduct_and_dispense(machine, price): machine.balance - price print(f扣款{price}元并启动出货电机) # 状态转移表配置 (当前状态, 事件) - (下一状态, 守卫条件函数, 动作函数) transition_table { (State.IDLE, Event.COIN_INSERTED): { next_state: State.COIN_INSERTED, guard: None, # 无条件 action: action_show_balance }, (State.COIN_INSERTED, Event.ITEM_SELECTED): { next_state: State.DISPENSING, guard: lambda m, p: m.balance p, # 守卫条件余额足够 action: action_deduct_and_dispense }, (State.COIN_INSERTED, Event.CANCEL): { next_state: State.RETURNING_CHANGE, guard: None, action: lambda m, _: print(取消计算找零金额) }, (State.DISPENSING, Event.DISPENSE_COMPLETED): { next_state: State.RETURNING_CHANGE, # 简化假设总有找零 guard: None, action: lambda m, _: print(出货电机停止) }, # ... 其他转移规则 } class TableDrivenVendingMachine: def __init__(self): self.current_state State.IDLE self.balance 0 self.transition_table transition_table def dispatch(self, event, **data): key (self.current_state, event) if key not in self.transition_table: print(f未定义的状态-事件组合: {key}) return rule self.transition_table[key] # 检查守卫条件 if rule[guard] is not None and not rule[guard](self, data.get(price, 0)): print(守卫条件不满足转换被阻止) return # 执行动作 if rule[action]: rule[action](self, data.get(price, 0)) # 转换状态 self.current_state rule[next_state] print(f状态转换: {key[0].name} - {self.current_state.name}) # 使用示例 vm TableDrivenVendingMachine() vm.dispatch(Event.COIN_INSERTED, amount5) vm.dispatch(Event.ITEM_SELECTED, price10) # 余额不足守卫条件阻止 vm.dispatch(Event.ITEM_SELECTED, price3) # 成功转换优点极其灵活。转移逻辑与业务代码分离可以通过修改配置数据甚至从文件或数据库加载来改变行为无需重新编译。非常适合规则频繁变化或需要动态更新的系统。缺点配置表可能变得庞大维护时需要小心。动作函数如果过于复杂可能会破坏解耦性。选型建议对于快速验证或小于5个状态的场景用switch-case。对于中等复杂度、状态逻辑独立且明确的系统用状态模式它更面向对象易于理解和测试。对于非常复杂、规则需动态配置或由非技术人员维护的场景如游戏技能、工单流程表驱动是更优选择。在实际项目中我见过将状态模式和表驱动结合使用的案例用状态类封装自身复杂的子逻辑用顶层表驱动管理状态间的宏观流转。5. 深入实战不同领域的状态机应用剖析状态机绝非玩具它在工业界各个角落都发挥着基石作用。下面我们结合网络热词看看它在不同领域的典型形态和实现特点。5.1 嵌入式与硬件描述语言Verilog/FPGA中的三段式状态机在FPGA和数字电路设计中状态机是描述时序逻辑的核心。Verilog中常用的“三段式”写法因其结构清晰、易于综合和避免毛刺而备受推崇。module fsm_example( input wire clk, input wire rst_n, input wire coin, input wire select, output reg dispense, output reg return_change ); // 第一段状态定义与状态寄存器声明 parameter IDLE 2b00; parameter COIN_IN 2b01; parameter DISPENSE 2b10; parameter RETURN_C 2b11; reg [1:0] current_state, next_state; // 第二段组合逻辑根据当前状态和输入确定下一状态 always (*) begin next_state current_state; // 默认保持当前状态 case(current_state) IDLE: if(coin) next_state COIN_IN; COIN_IN: begin if(select) next_state DISPENSE; else if(~coin) next_state IDLE; // 假设coin为脉冲信号 end DISPENSE: next_state RETURN_C; // 出货完成后自动进入找零 RETURN_C: next_state IDLE; default: next_state IDLE; endcase end // 第三段时序逻辑在时钟边沿进行状态转换并输出 always (posedge clk or negedge rst_n) begin if(!rst_n) begin current_state IDLE; dispense 1b0; return_change 1b0; end else begin current_state next_state; // 输出逻辑摩尔型输出仅与当前状态有关 case(current_state) DISPENSE: begin dispense 1b1; return_change 1b0; end RETURN_C: begin dispense 1b0; return_change 1b1; end default: begin dispense 1b0; return_change 1b0; end endcase end end endmodule三段式的精髓状态定义段用参数定义状态编码二进制、独热码等。独热码One-Hot在FPGA中资源利用和时序性能上常有优势。次态组合逻辑段用always (*)描述状态转移条件。这是一个纯组合逻辑块其输出next_state是当前状态和输入信号的函数。现态时序逻辑与输出段在时钟边沿将next_state打入current_state寄存器。输出可以在此段根据current_state摩尔机或current_state与输入米利机产生。踩坑记录在早期我曾将次态逻辑和输出逻辑混在同一个时序always块里这被称为“两段式”。虽然代码短但容易产生毛刺并且综合器有时会推断出锁存器Latch导致难以预料的行为。三段式严格区分了组合和时序逻辑是更可靠、更专业的写法。另外对于AUTOSAR等汽车电子架构中的网络管理状态机其设计思想与此一脉相承但规范更为严格需要考虑总线睡眠、唤醒等复杂场景。5.2 后端业务逻辑Spring Statemachine与订单生命周期在后端开发中复杂的业务对象如订单、工单、审核流其生命周期非常适合用状态机来管理。Spring Statemachine是Spring生态中一个强大的状态机框架。假设一个电商订单状态待支付-已支付-已发货-已完成/已取消。// 1. 定义状态和事件枚举 public enum OrderStates { UNPAID, // 待支付 PAID, // 已支付 SHIPPED, // 已发货 DONE, // 已完成 CANCELLED // 已取消 } public enum OrderEvents { PAY, // 支付 SHIP, // 发货 RECEIVE, // 确认收货 CANCEL // 取消 } // 2. 配置状态机 Configuration EnableStateMachine public class OrderStateMachineConfig extends StateMachineConfigurerAdapterOrderStates, OrderEvents { Override public void configure(StateMachineStateConfigurerOrderStates, OrderEvents states) throws Exception { states .withStates() .initial(OrderStates.UNPAID) .states(EnumSet.allOf(OrderStates.class)) .end(OrderStates.DONE) // 终止状态 .end(OrderStates.CANCELLED); } Override public void configure(StateMachineTransitionConfigurerOrderStates, OrderEvents transitions) throws Exception { transitions .withExternal() .source(OrderStates.UNPAID).target(OrderStates.PAID) .event(OrderEvents.PAY) .and() .withExternal() .source(OrderStates.PAID).target(OrderStates.SHIPPED) .event(OrderEvents.SHIP) .and() .withExternal() .source(OrderStates.SHIPPED).target(OrderStates.DONE) .event(OrderEvents.RECEIVE) .and() .withExternal() .source(OrderStates.UNPAID).target(OrderStates.CANCELLED) .event(OrderEvents.CANCEL) .guard(orderGuard()) // 可以添加守卫条件如超时未支付才允许取消 .and(); } Bean public GuardOrderStates, OrderEvents orderGuard() { return context - { // 从Message Headers或Extended State中获取业务参数进行判断 return true; // 简化示例 }; } } // 3. 在Service中使用 Service public class OrderService { Autowired private StateMachineOrderStates, OrderEvents stateMachine; public void payOrder(Long orderId) { // 1. 发送事件触发状态转换 boolean accepted stateMachine.sendEvent(OrderEvents.PAY); if (accepted) { // 2. 状态转换成功后执行业务逻辑如更新数据库、发消息等 orderRepository.updateStatus(orderId, OrderStates.PAID); // 可以监听状态转换事件 OnTransition 来执行特定动作 } else { throw new IllegalStateException(当前订单状态不允许支付); } } }Spring Statemachine的优势声明式配置通过Java Config或DSL清晰定义状态流。分层状态机支持子状态机可以建模复杂的状态层次例如“配送中”状态可能有“已揽件”、“运输中”、“派送中”等子状态。扩展状态Extended State除了离散状态还可以用变量如orderAmount,retryCount来存储上下文信息供守卫条件和动作使用。事件监听可以方便地在状态进入、退出、转换时触发自定义动作。持久化支持将状态机状态持久化到数据库这对于分布式环境下保证状态一致性至关重要。实战经验在微服务架构中状态机是保证业务逻辑一致性的利器。我们将核心业务实体的状态机部署为独立的“状态机服务”或嵌入在领域服务中所有状态变更必须通过发送事件到状态机来完成。这强制了业务规则的集中管理避免了状态判断逻辑散落在代码各处。同时状态机的所有转换日志天然就是一份完整的业务审计追踪。5.3 游戏与前端分层状态机与行为控制在游戏开发或复杂UI交互中状态机常用于控制角色行为、动画或页面流程。Stateless等库提供了轻量级的状态机实现而分层状态机HFSM的概念则更为强大。以游戏角色为例顶层状态有闲置、移动、攻击、死亡。攻击状态下又可以细分为举起武器、挥动、收回等子状态。// 使用一个简单的状态机库概念示例 class CharacterStateMachine { constructor(character) { this.character character; this.states { idle: new IdleState(this), move: new MoveState(this), attack: new AttackState(this), // AttackState本身可能也是一个状态机 die: new DieState(this) }; this.currentState this.states.idle; this.currentState.enter(); } transitionTo(stateName) { if (this.currentState.canTransitionTo(stateName)) { this.currentState.exit(); this.currentState this.states[stateName]; this.currentState.enter(); } } update(deltaTime) { this.currentState.update(deltaTime); // 状态机自身也可以根据条件自动触发转换 if (this.character.health 0 this.currentState.name ! die) { this.transitionTo(die); } } } class IdleState { constructor(fsm) { this.fsm fsm; this.name idle; } enter() { console.log(进入闲置状态播放待机动画); } exit() { console.log(退出闲置状态); } update(deltaTime) { // 检查输入或环境决定是否转换 if (inputManager.isMoveKeyPressed()) { this.fsm.transitionTo(move); } else if (inputManager.isAttackKeyPressed()) { this.fsm.transitionTo(attack); } } canTransitionTo(stateName) { return true; } } // AttackState 可能内部又管理着举起、挥动、收回子状态 class AttackState { constructor(fsm) { this.fsm fsm; this.name attack; // 内部嵌套一个用于管理攻击子状态的状态机 this.subStates { raise: ..., swing: ..., recover: ... }; this.currentSubState this.subStates.raise; } enter() { this.currentSubState.enter(); } update(deltaTime) { this.currentSubState.update(deltaTime); // 当内部子状态机完成如攻击动画播放完毕退出攻击大状态 if (this.currentSubState.isFinished()) { this.fsm.transitionTo(idle); } } }分层状态机的价值它允许状态拥有子状态子状态可以继承父状态的行为如进入/退出动作并可以覆盖父状态对某些事件的处理。这极大地减少了重复代码并让复杂行为建模变得模块化。在机器人状态机设计中这种分层思想更是必不可少比如一个“巡逻”状态可能包含“规划路径”、“移动至点A”、“等待”、“移动至点B”等子状态。6. 设计陷阱与最佳实践即使理解了原理在实际应用中仍会踩坑。下面分享几个我总结的关键实践和常见陷阱。6.1 状态爆炸与如何化简最头疼的问题莫过于状态数量失控。如果为每一个细微差别都创建一个状态状态机将难以维护。解决方法使用扩展状态变量将一些信息从状态名中剥离用变量表示。例如不用为“金额不足1元”、“金额不足2元”创建不同状态而是用一个balance变量和“已投币”状态在守卫条件中判断balance price。引入子状态机将一组紧密相关的状态封装成一个子状态机对外呈现为一个高级状态。如上文的“攻击”状态。重新审视建模问自己这些真的是“状态”吗还是仅仅是同一状态下的不同“数据”状态应是互斥的、稳定的阶段。6.2 确保状态转换的原子性与一致性在并发或分布式环境下状态转换可能被打断或重复执行导致状态不一致。在数据库层面加锁转换前使用悲观锁如SELECT FOR UPDATE或乐观锁版本号锁定记录。使用幂等事件事件应可重复发送而不产生副作用。在状态机端可以检查“当前状态是否允许处理此事件”如果已是目标状态则直接返回成功。实现状态机快照与持久化定期或将关键转换后的状态机完整上下文持久化。在Spring Statemachine中可以利用其StateMachinePersist接口。这样在系统重启后能恢复到断点。6.3 测试策略覆盖所有路径状态机的可测试性是其一大优点。测试应覆盖所有有效路径按照状态转移表测试每一个定义的转换。所有无效事件在每个状态下发送所有未定义的事件确保系统行为符合预期通常是忽略或报错。守卫条件测试守卫条件为真和为假两种情况。动作副作用验证状态转换时执行的动作是否正确。初始化与终止测试状态机能否正确初始化和进入终止状态。可以针对状态转移表自动生成测试用例实现高覆盖率。6.4 日志与调试给状态机装上“黑匣子”复杂的业务状态机出问题时清晰的日志是救命稻草。务必记录状态机ID如订单号。时间戳。收到的事件及其载荷。转换前状态。执行的守卫条件及其结果。执行的动作。转换后状态。这形成了一个完整的审计追踪对于排查“这个订单为什么卡在这里”之类的问题无比重要。许多状态机框架都提供了内置的监听器或拦截器来方便地添加日志。状态机是一种强大而优雅的建模工具它将混乱的条件分支逻辑规整为清晰的状态网络。无论是硬件描述中的三段式还是Spring中的StateMachine或是游戏里的行为树其内核都是对“状态-事件-响应”这一模式的抽象。掌握它并不意味着你要在所有地方都用上它而是当你在面对复杂的、有明确生命周期的业务流程时能多一种清晰、健壮的设计选择。从我个人的经验来看在项目初期多花一点时间画状态图、填转移表后期在维护和排查问题上节省的时间将是巨大的。下次当你再看到一堆缠绕的if-else时不妨停下来想想“这里是不是藏着一个状态机”

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价