学设计模式的时候,GoF(Gang of Four,四人组)的经典状态模式几乎是必考内容。它看起来完美解决了那些又大又丑的switch语句,面试时画个类图,面试官频频点头。
但真到了写业务代码的时候,很多人会撞上一堵墙——这模式怎么越用越别扭?每个状态类都跟其他状态类纠缠不清,改一个需求要动一堆文件。如果你也有这种感觉,那你的直觉没错。
问题出在哪?GoF状态模式的核心思想是“把工作分散出去”:主对象把逻辑委托给各个状态对象,但状态类自己负责触发下一个状态的切换。听起来很合理,对吧?
举个最简单的例子:红绿灯。红灯变绿灯,绿灯变黄灯,黄灯变红灯,无限循环。
class RedState implements TrafficLightState {change(context: TrafficLight): void {console.log("RED light, Stop");context.setState(new GreenState()); // 这里就耦合了!}问题立刻暴露:RedState被迫知道了GreenState的存在。红绿灯这种封闭循环还好,规则永远不会变,耦合就耦合了。但真实业务哪有这么简单?
业务规则一变,状态模式就崩了
想象一下,市议会突然出新规定:午夜到凌晨5点,红绿灯必须闪黄灯。这时候你得打开RedState和YellowState,加时间判断,再加一个新的闪烁状态。状态越多,代码越乱,每个类都要知道其他所有类的存在。
真实世界的流程从来不是直线。一个电商订单不会老老实实从“待付款”走到“已发货”再到“已签收”。它可能从“待付款”直接跳到“已取消”,也可能从“已发货”变成“已退货”。
如果用GoF状态模式,你的PendingState(待付款状态)得知道多少个其他状态?它要处理支付成功、取消订单、超时关闭……这个类会膨胀到你不想看它。
FSM:把控制权收回来
这时候就该有限状态机(FSM,Finite State Machine)出场了。它的核心思路正好相反:集中控制。
状态类变得非常简单,只负责自己内部该干什么。状态之间的跳转逻辑,全部交给一个中央控制器——我们叫它Orchestrator(编排器)。
const orderRules = {PENDING: {PAID: "SHIPPED",CANCELLED: "CANCELLED",},SHIPPED: {ARRIVED: "DELIVERED",RETURNED: "RETURNED",},class OrderController {currentState = "PENDING";handleEvent(event: string) {const nextState = orderRules[this.currentState]?.[event];if (nextState) {this.currentState = nextState;// 在这里执行状态逻辑} else {console.log("非法跳转!");}这段代码的精髓在于:状态转移规则被集中在一张表里。新增一个状态?加一行配置就行。禁止某个跳转?不写那条规则就行。所有状态之间的关系一目了然,再也不用翻遍每个状态类去找谁调用了谁。
什么时候该用哪个?
这不是说GoF状态模式一无是处。它适合状态数量少、转移关系固定、几乎不会变的场景——比如红绿灯、电梯控制、TCP连接状态。这些场景状态之间天然解耦,每个状态确实只需要关心自己。
但一旦你的业务规则频繁变化、状态跳转路径复杂、或者有大量条件分支,FSM就是更稳的选择。它把最容易变的部分(状态转移规则)集中管理,让状态类保持纯粹。
判断标准很简单:如果改一个状态需要动其他状态类,你就该考虑FSM了。别让教科书里的经典模式绑架你的真实代码。
热门跟贴