
Java 23 种设计模式从踩坑到精通 | 状态模式 —— if-else 满天飞让状态自己决定行为摘要当对象的行为需要根据内部状态动态改变时大量if-else或switch会让代码臃肿、难以扩展。状态模式将每个状态封装为独立的类让对象在状态变化时自动切换行为从而消除了冗长的条件判断。本文从订单状态流转的场景出发完整讲解状态模式的原理、UML、代码实现、与策略模式的区别并结合 Java 线程状态机、Spring State Machine、工作流引擎等应用帮你掌握“行为随状态变化”的设计精髓。️本文阅读地图3 分钟速览为什么一坨if-else订单状态判断一定会炸状态模式三角色拆解手写订单状态流转待支付→已支付→已发货→已完成简易交通灯红绿灯自动切换Java 线程状态机 / Spring State Machine / 工作流引擎如何体现面试必问状态 vs 策略到底怎么区分《Java 23 种设计模式从踩坑到精通》开篇系列介绍与目录 上一篇观察者模式 当前State 状态模式 下一篇策略模式 返回系列总目录文章目录Java 23 种设计模式从踩坑到精通 | 状态模式 —— if-else 满天飞让状态自己决定行为1. 从“订单状态”的一堆 if-else 说起1.1 你的场景该不该用状态模式2. 模式定义与 UML 结构图文解析配合上述 UML 图3. 代码实现订单状态流转3.1 抽象状态3.2 具体状态待支付3.3 具体状态已支付3.4 具体状态已发货3.5 其他状态已完成、已取消3.6 上下文订单3.7 客户端4. 代码实现简易交通灯4.1 抽象状态4.2 具体状态4.3 上下文交通灯4.4 客户端5. 状态模式 vs 策略模式6. 优缺点一览7. 框架与实践中的应用7.1 JDK 线程状态转换7.2 Spring State Machine7.3 工作流引擎Activiti、Camunda8. 面试必问 面试官追问连环炮9. 六大设计原则在状态模式中的体现 《Java 23 种设计模式从踩坑到精通》快速导航1. 从“订单状态”的一堆 if-else 说起电商系统中订单通常有多个状态待支付、已支付、已发货、已完成、已取消。每个状态下用户能进行的操作不同待支付可以支付、取消已支付可以发货、退款已发货可以确认收货已完成可以评价已取消什么也不能做。如果不加设计代码会变成这样voidhandle(Orderorder,Stringaction){if(待支付.equals(order.getStatus())){if(支付.equals(action)){// 支付逻辑order.setStatus(已支付);}elseif(取消.equals(action)){order.setStatus(已取消);}}elseif(已支付.equals(order.getStatus())){if(发货.equals(action)){...}elseif(退款.equals(action)){...}}// ...}随着状态增多if-else会无限膨胀新增一个状态需要修改所有相关判断极易出错。更麻烦的是状态间的合法流转规则隐藏在分散的判断条件中难以看清全貌。状态模式State Pattern正是为这种“行为随状态变化”的场景而生它将每个状态封装成一个独立的类把与该状态相关的所有行为都放在这个类中并在状态类中定义允许的转换从而让对象在运行时通过改变状态来改变行为。1.1 你的场景该不该用状态模式判断标准是 → 用状态模式否 → 用其他方式对象的行为完全由内部状态决定✅❌状态数量较多≥3个且可能继续增加✅❌状态之间的转换规则复杂需要显式管理✅❌状态极少且固定如只有开/关两种❌简单的 if-else 或枚举即可2. 模式定义与 UML 结构状态模式允许一个对象在其内部状态改变时改变它的行为对象看起来似乎修改了它的类。它属于行为型设计模式。图文解析配合上述 UML 图状态模式的核心角色上下文Context持有当前状态对象定义客户端感兴趣的接口并将与状态相关的请求委托给当前状态对象处理。它不知道自己处于哪个具体状态只知道调用当前状态的handle()。抽象状态State定义一个接口封装与Context的一个特定状态相关的行为。每个方法代表该状态下可以执行的操作。具体状态ConcreteStateA/ConcreteStateB实现一个与Context某个状态相关的行为并在需要时发起状态转换——调用context.setState(newState)切换到下一个状态。核心机制状态模式把“在不同状态下做不同的事”这个横切逻辑从一长串if-else拆分到一个个独立的状态类中。上下文对象不需要知道任何状态转换逻辑它只需把请求委派给当前状态对象。状态类自己决定何时切换、切换到哪个状态。3. 代码实现订单状态流转3.1 抽象状态publicinterfaceOrderState{voidpay(OrderContextcontext);voidcancel(OrderContextcontext);voidship(OrderContextcontext);voidconfirm(OrderContextcontext);}白话订单状态必须能响应四种操作——支付、取消、发货、确认收货。每个状态对这些操作的响应不同。3.2 具体状态待支付publicclassPendingPaymentStateimplementsOrderState{Overridepublicvoidpay(OrderContextcontext){System.out.println(支付成功订单状态变为【已支付】);context.setState(newPaidState());}Overridepublicvoidcancel(OrderContextcontext){System.out.println(订单已取消);context.setState(newCancelledState());}Overridepublicvoidship(OrderContextcontext){thrownewIllegalStateException(待支付订单无法发货);}Overridepublicvoidconfirm(OrderContextcontext){thrownewIllegalStateException(待支付订单无法确认收货);}}白话待支付状态下只有“支付”和“取消”是合法操作。支付后自动切换到已支付状态取消后切换到已取消状态。3.3 具体状态已支付publicclassPaidStateimplementsOrderState{Overridepublicvoidpay(OrderContextcontext){thrownewIllegalStateException(已支付订单无需重复支付);}Overridepublicvoidcancel(OrderContextcontext){thrownewIllegalStateException(已支付订单无法取消请申请退款);}Overridepublicvoidship(OrderContextcontext){System.out.println(订单已发货);context.setState(newShippedState());}Overridepublicvoidconfirm(OrderContextcontext){thrownewIllegalStateException(已支付订单未发货无法确认收货);}}白话已支付状态下只能发货不能支付、不能取消、不能确认收货。状态类自己充当“门卫”。3.4 具体状态已发货publicclassShippedStateimplementsOrderState{Overridepublicvoidpay(OrderContextcontext){thrownewIllegalStateException(已发货订单无需支付);}Overridepublicvoidcancel(OrderContextcontext){thrownewIllegalStateException(已发货订单无法取消);}Overridepublicvoidship(OrderContextcontext){thrownewIllegalStateException(已发货订单无需重复发货);}Overridepublicvoidconfirm(OrderContextcontext){System.out.println(确认收货订单完成);context.setState(newCompletedState());}}3.5 其他状态已完成、已取消publicclassCompletedStateimplementsOrderState{Overridepublicvoidpay(OrderContextcontext){thrownewIllegalStateException(已完成订单无需支付);}Overridepublicvoidcancel(OrderContextcontext){thrownewIllegalStateException(已完成订单无法取消);}Overridepublicvoidship(OrderContextcontext){thrownewIllegalStateException(已完成订单无需发货);}Overridepublicvoidconfirm(OrderContextcontext){thrownewIllegalStateException(已完成订单无需确认);}}publicclassCancelledStateimplementsOrderState{Overridepublicvoidpay(OrderContextcontext){thrownewIllegalStateException(已取消订单无法支付);}Overridepublicvoidcancel(OrderContextcontext){thrownewIllegalStateException(已取消订单无法再次取消);}Overridepublicvoidship(OrderContextcontext){thrownewIllegalStateException(已取消订单无法发货);}Overridepublicvoidconfirm(OrderContextcontext){thrownewIllegalStateException(已取消订单无法确认);}}白话终态已完成/已取消的所有操作都抛异常——状态类自己就是最好的“防御性编程”。3.6 上下文订单publicclassOrderContext{privateOrderStatestate;publicOrderContext(){this.statenewPendingPaymentState();// 初始状态待支付}publicvoidsetState(OrderStatestate){this.statestate;}publicOrderStategetState(){returnstate;}// 将操作委托给当前状态publicvoidpay(){state.pay(this);}publicvoidcancel(){state.cancel(this);}publicvoidship(){state.ship(this);}publicvoidconfirm(){state.confirm(this);}}白话订单自己不判断状态直接把请求转发给当前状态对象。状态对象处理请求后如果需要会调用context.setState()切换状态。3.7 客户端OrderContextordernewOrderContext();order.pay();// 支付成功订单状态变为【已支付】order.ship();// 订单已发货order.confirm();// 确认收货订单完成order.cancel();// 抛出异常已完成订单无法取消所有状态转换规则被清晰封装在各个状态类中上下文类干净得只剩委托。新增一个状态如“退款中”只需新增一个状态类修改相关状态类的转换逻辑上下文和已有状态类无需修改。4. 代码实现简易交通灯另一个经典例子是交通灯绿→黄→红循环切换。状态数量少但转换规则明确。4.1 抽象状态interfaceTrafficLightState{voidswitchLight(TrafficLightlight);}4.2 具体状态classGreenStateimplementsTrafficLightState{OverridepublicvoidswitchLight(TrafficLightlight){System.out.println(绿灯 → 黄灯);light.setState(newYellowState());}}classYellowStateimplementsTrafficLightState{OverridepublicvoidswitchLight(TrafficLightlight){System.out.println(黄灯 → 红灯);light.setState(newRedState());}}classRedStateimplementsTrafficLightState{OverridepublicvoidswitchLight(TrafficLightlight){System.out.println(红灯 → 绿灯);light.setState(newGreenState());}}白话每个灯都知道自己的下一个灯是谁。绿灯→黄灯→红灯→绿灯循环往复。4.3 上下文交通灯classTrafficLight{privateTrafficLightStatestate;publicTrafficLight(){this.statenewRedState();// 初始红灯}publicvoidsetState(TrafficLightStatestate){this.statestate;}publicvoidswitchLight(){state.switchLight(this);}}4.4 客户端TrafficLightlightnewTrafficLight();light.switchLight();// 红灯 → 绿灯light.switchLight();// 绿灯 → 黄灯light.switchLight();// 黄灯 → 红灯5. 状态模式 vs 策略模式这是面试中极容易混淆的一对对比维度状态模式策略模式目的根据对象内部状态自动改变行为根据不同情况选择不同算法谁决定切换状态类自己决定何时切换到另一个状态客户端显式选择策略关注点状态之间的流转和转换算法的替换与组合上下文感知状态知道上下文的存在并可能调用上下文方法策略通常不知道上下文的存在典型应用订单状态机、线程生命周期支付方式、排序算法、折扣策略简单记忆状态模式是“被动切换”状态自己决定下一步策略模式是“主动选择”客户端选择算法。状态模式中状态类之间通常有明确的转换关系策略模式中策略类之间完全独立。6. 优缺点一览优点缺点消除庞大的条件判断将每个状态的行为分散到独立的类中类数量增加每个状态一个类状态多时类数量多符合开闭原则新增状态只需新增一个状态类状态类之间可能产生依赖转换逻辑需要知道目标状态类状态转换显式化合法流转规则集中在状态类中上下文与状态双向依赖状态类需要引用上下文行为可动态切换运行时切换状态即可改变对象行为简单状态机过度设计2-3个固定状态用枚举更简洁7. 框架与实践中的应用7.1 JDK 线程状态转换Java 线程的 6 个状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED本身就是状态模式的体现。Thread.start()、Object.wait()、notify()等操作都会导致线程状态的自动流转。7.2 Spring State MachineSpring 提供了专门的状态机框架支持定义状态、事件、转换、守卫、动作等复杂状态流转逻辑广泛应用于订单、工作流等场景。7.3 工作流引擎Activiti、Camunda工作流引擎的核心就是状态模式将业务流程中的每个节点定义为一个状态通过提交、审批、驳回等操作驱动状态转换。8. 面试必问 面试官追问连环炮基础必问状态模式如何解决 if-else 问题→ 将每个分支行为封装为独立状态类通过多态消除条件判断。状态模式与策略模式的区别→ 状态模式状态自己决定转换策略模式由客户端切换策略。JDK 哪里用了状态模式→ 线程的状态转换NEW → RUNNABLE → WAITING → ...。面试官追问“状态模式中状态类为什么要持有上下文引用” 以便在状态转换时调用context.setState()切换到新状态。没有上下文引用状态就无法改变对象的行为。“如果状态转换逻辑很复杂状态类会膨胀怎么办” 使用状态机框架如 Spring State Machine将状态、事件、转换规则配置化避免手写大量状态类。“状态模式和策略模式能配合使用吗” 可以。状态决定“什么时候该做什么”策略决定“做这件事用哪种算法”。例如订单状态决定“该结算了”策略决定“用满减还是打折结算”。恭喜如果你能立刻画出订单的状态流转图并清楚状态与策略的本质区别你已经掌握了行为型模式中“行为随状态变化”的设计精髓。9. 六大设计原则在状态模式中的体现设计原则在状态模式中的体现单一职责原则SRP每个状态类只负责一种状态下的行为逻辑开闭原则OCP新增状态无需修改上下文只需新增状态类里氏替换原则LSP所有状态类都实现State接口可相互替换依赖倒置原则DIP上下文依赖抽象State接口不依赖具体状态类接口隔离原则ISPState接口定义该状态下所有可能的操作迪米特法则LoD上下文只知道当前状态不知道状态如何实现转换 《Java 23 种设计模式从踩坑到精通》快速导航开篇系列介绍与目录上一篇观察者模式 —— 发布-订阅你每天都在用的模式当前状态模式 —— if-else 满天飞让状态自己决定行为你在这里下一篇策略模式 —— 算法族的封装与切换告别 if-else创建型模式汇总单例、工厂、建造者、原型结构型模式汇总适配器、装饰器、代理……行为型模式汇总观察者、策略、模板方法…… 关注《Java 23 种设计模式从踩坑到精通》用 25 篇文章彻底吃透设计模式。福利预告全系列代码及 UML 源码将在完结时统一打包开放点击「关注」「收藏」第一时间获取。下一篇策略模式算法族的封装与切换告别 if-else 除了设计模式我也在深挖智能物流实战WMS、托盘调度、机器学习落地。欢迎点击头像看看专栏 《出版社物流WMS智能调度实战》。技术相通思路可鉴。