当前位置: 首页 > news >正文

Java 状态机详解:三种实现方式优雅消灭 if-else 嵌套

Java 状态机详解:三种实现方式优雅消灭 if-else 嵌套

在 Java 开发中,状态机(Finite State Machine,FSM)是一种经典的设计模式,用于管理对象的有限状态和状态之间的转换。它特别适合处理复杂业务逻辑,比如订单流程(待支付 → 已支付 → 发货 → 收货)、用户认证(未登录 → 登录中 → 已登录)、游戏角色状态(idle → running → jumping)等。

为什么需要状态机?
传统的 if-else 嵌套容易导致代码膨胀、难以维护(“意大利面代码”),尤其当状态和事件增多时。状态机通过将状态和转换逻辑解耦,能让代码更清晰、可扩展、可测试。

下面,我们先看一个 if-else 嵌套的“坏”例子(订单状态机),然后介绍三种优雅实现方式。每种方式都附带完整代码示例(基于 Java 8+,可直接复制运行)。这些方式都能“消灭” if-else 嵌套,但侧重点不同。

问题示例:if-else 嵌套的“坏”代码

假设一个订单系统,有状态:PENDING(待支付)、PAID(已支付)、SHIPPED(已发货)、DELIVERED(已收货)。事件:支付、发货、确认收货。

publicclassOrder{privateStringstate="PENDING";// 初始状态publicvoidpay(){if("PENDING".equals(state)){state="PAID";System.out.println("支付成功,状态变为 PAID");}elseif("PAID".equals(state)){System.out.println("已支付,无需重复");}else{System.out.println("无效操作");}}publicvoidship(){if("PAID".equals(state)){state="SHIPPED";System.out.println("发货成功,状态变为 SHIPPED");}elseif("PENDING".equals(state)){System.out.println("请先支付");}else{System.out.println("无效操作");}}publicvoiddeliver(){if("SHIPPED".equals(state)){state="DELIVERED";System.out.println("收货成功,状态变为 DELIVERED");}else{System.out.println("无效操作");}}publicstaticvoidmain(String[]args){Orderorder=newOrder();order.pay();// 支付成功order.ship();// 发货成功order.deliver();// 收货成功}}

问题:每个方法都有一堆 if-else;新增状态/事件时,所有方法都要改;容易出错、难以扩展。

方式一:枚举 + switch-case(最简单、轻量级)

使用枚举定义状态和事件,在一个方法中用 switch 处理所有转换。适合状态不多(<10个)的简单场景。

优点:代码集中、易理解、无需额外类。
缺点:switch 块可能变长;不适合复杂动作(每个 case 只适合简单逻辑)。
适用:小项目、快速原型。

publicclassOrderEnum{enumState{PENDING,PAID,SHIPPED,DELIVERED}enumEvent{PAY,SHIP,DELIVER}privateStatecurrentState=State.PENDING;publicvoidhandleEvent(Eventevent){switch(currentState){casePENDING:if(event==Event.PAY){currentState=State.PAID;System.out.println("支付成功,状态变为 PAID");}else{System.out.println("无效操作");}break;casePAID:if(event==Event.SHIP){currentState=State.SHIPPED;System.out.println("发货成功,状态变为 SHIPPED");}else{System.out.println("无效操作");}break;caseSHIPPED:if(event==Event.DELIVER){currentState=State.DELIVERED;System.out.println("收货成功,状态变为 DELIVERED");}else{System.out.println("无效操作");}break;caseDELIVERED:System.out.println("订单已完成,无操作");break;default:System.out.println("未知状态");}}publicstaticvoidmain(String[]args){OrderEnumorder=newOrderEnum();order.handleEvent(Event.PAY);// 支付成功order.handleEvent(Event.SHIP);// 发货成功order.handleEvent(Event.DELIVER);// 收货成功}}

扩展提示:如果动作复杂,可以在 case 中调用私有方法执行具体逻辑。

方式二:状态模式(State Pattern,经典 OOP 方式)

使用接口定义状态行为,每个状态一个实现类。订单类持有一个状态对象,根据事件委托给当前状态处理。

优点:每个状态独立类,易扩展(新增状态只需加类);符合开闭原则(修改关闭,扩展开放)。
缺点:类爆炸(状态多时类文件多);初始代码量大。
适用:中等复杂场景,企业级系统。

// 状态接口interfaceOrderState{voidpay(OrderContextcontext);voidship(OrderContextcontext);voiddeliver(OrderContextcontext);}// 上下文类(订单)classOrderContext{privateOrderStatecurrentState;publicOrderContext(){currentState=newPendingState();// 初始状态}publicvoidsetState(OrderStatestate){this.currentState=state;}publicvoidpay(){currentState.pay(this);}publicvoidship(){currentState.ship(this);}publicvoiddeliver(){currentState.deliver(this);}}// 具体状态类(Pending)classPendingStateimplementsOrderState{@Overridepublicvoidpay(OrderContextcontext){System.out.println("支付成功,状态变为 PAID");context.setState(newPaidState());}@Overridepublicvoidship(OrderContextcontext){System.out.println("请先支付");}@Overridepublicvoiddeliver(OrderContextcontext){System.out.println("无效操作");}}// PaidState(类似,其他状态类省略)classPaidStateimplementsOrderState{@Overridepublicvoidpay(OrderContextcontext){System.out.println("已支付,无需重复");}@Overridepublicvoidship(OrderContextcontext){System.out.println("发货成功,状态变为 SHIPPED");context.setState(newShippedState());}@Overridepublicvoiddeliver(OrderContextcontext){System.out.println("无效操作");}}// ShippedState 和 DeliveredState 类似...publicclassOrderStatePattern{publicstaticvoidmain(String[]args){OrderContextorder=newOrderContext();order.pay();// 支付成功order.ship();// 发货成功order.deliver();// 无效操作(需实现 ShippedState)}}

扩展提示:每个状态类可以持有上下文数据;用枚举管理状态类实例(单例)。

方式三:表驱动法(使用 Map 的策略模式)

用 Map 映射“当前状态 + 事件”到“下一个状态 + 动作”。适合状态转换规则明确的场景。

优点:配置化、易修改(Map 可以从配置文件加载);无 switch,无多类。
缺点:动作复杂时需用 Lambda 或函数接口;可读性稍差。
适用:规则多、需动态配置的系统(如游戏 AI、流程引擎)。

importjava.util.HashMap;importjava.util.Map;importjava.util.function.Consumer;publicclassOrderTableDriven{enumState{PENDING,PAID,SHIPPED,DELIVERED}enumEvent{PAY,SHIP,DELIVER}privateStatecurrentState=State.PENDING;// 动作接口(Consumer 消费上下文,这里简化无上下文)privatestaticclassTransition{StatenextState;Consumer<Void>action;Transition(Statenext,Consumer<Void>act){nextState=next;action=act;}}privatefinalMap<State,Map<Event,Transition>>stateMachine=newHashMap<>();publicOrderTableDriven(){// 配置状态机表Map<Event,Transition>pendingMap=newHashMap<>();pendingMap.put(Event.PAY,newTransition(State.PAID,v->System.out.println("支付成功")));stateMachine.put(State.PENDING,pendingMap);Map<Event,Transition>paidMap=newHashMap<>();paidMap.put(Event.SHIP,newTransition(State.SHIPPED,v->System.out.println("发货成功")));stateMachine.put(State.PAID,paidMap);Map<Event,Transition>shippedMap=newHashMap<>();shippedMap.put(Event.DELIVER,newTransition(State.DELIVERED,v->System.out.println("收货成功")));stateMachine.put(State.SHIPPED,shippedMap);// DELIVERED 无转换stateMachine.put(State.DELIVERED,newHashMap<>());}publicvoidhandleEvent(Eventevent){Map<Event,Transition>transitions=stateMachine.get(currentState);if(transitions==null||!transitions.containsKey(event)){System.out.println("无效操作");return;}Transitiontrans=transitions.get(event);trans.action.accept(null);// 执行动作currentState=trans.nextState;}publicstaticvoidmain(String[]args){OrderTableDrivenorder=newOrderTableDriven();order.handleEvent(Event.PAY);// 支付成功order.handleEvent(Event.SHIP);// 发货成功order.handleEvent(Event.DELIVER);// 收货成功}}

扩展提示:动作可以用 Function 或 Runnable;Map 可以从 JSON/YAML 加载,实现配置驱动。

总结与选择建议
  • 方式一(枚举 + switch):入门级,适合小状态机(<5状态)。
  • 方式二(状态模式):中高级,适合复杂动作(每个状态有独立逻辑)。
  • 方式三(表驱动):高级,适合规则多、需配置化的系统。

三种方式都比 if-else 优雅,选择基于项目复杂度。实际开发中,可结合 Spring StateMachine 框架(企业级状态机库)进一步简化。

如果你想看更多代码细节、测试用例,或对比其他方式(如策略模式变体),告诉我你的需求,我继续展开~ 😊

http://www.jsqmd.com/news/336722/

相关文章:

  • 从人工到智能:AI 薪酬管理软件提升企业管理效能的底层逻辑
  • 2026郑州股权架构设计公司综合评估:6家顶尖机构深度解析 - 2026年企业推荐榜
  • 千匠B2B商城解决方案:全链路数字化能力重构渠道生态 - 圆圆小达人
  • 大米电视 6.6.9| 内含优质频道,港台高清不卡顿,超2000个直播频道
  • 多变量Pearson相关系数计算和Pearson相关系数的统计检验
  • 鲸鱼优化算法(WOA)文章复现:《改进鲸鱼优化算法在机械臂时间最优轨迹规划的应用_赵晶》 策略为
  • 根据变量之间变化的方向,相关关系可分为正相关(Positive Correlation)和负相关(Negative Correlation)
  • C#实现Modbus TCP通讯测试软件
  • 2026挖掘机培训行业报告:西北地区实训基地选择与就业前景解析 - 深度智识库
  • 支持单列、多列等布局自定义表单系统源码 带完整的搭建部署教程
  • leetcode 889. Construct Binary Tree from Preorder and Postorder Traversal 根据前序和后序遍历构造二叉树
  • 2026少儿编程品牌怎么选?十大品牌综合实力榜出炉!为家长提供专业参考 - 匠言榜单
  • 脊柱外科手术显微镜哪家好?深度盘点新天医疗等六家代表企业 - 企师傅推荐官
  • 前特斯拉 AI 总监:AI 编程最大的谎言,是 “提效”
  • 支持在线收款的自定义表单系统源码
  • 2026年耐火材料厂家最新推荐榜:耐火砖/高铝砖/刚玉砖/磷酸盐砖等类型优质厂家权威榜单发布保温砖/浇注料/可塑料/莫来石砖厂家推荐 - 深度智识库
  • 2026年湖北纸塑复合袋定制服务商综合实力深度解析 - 2026年企业推荐榜
  • 2026挖掘机培训学校深度测评:如何选对学校,开启高薪职业生涯 - 深度智识库
  • 灵活自定义表单系统源码,满足多样化业务需求的终极工具
  • 2026年目前有名的推拉窗品牌推荐,推拉窗/侧压平移推拉窗/平移断桥提升窗/断桥铝门窗,推拉窗实力厂家有哪些 - 品牌推荐师
  • USACO历年青铜组真题解析 | 2023年2月
  • 2026年山西文博展柜机构综合实力TOP5盘点 - 2026年企业推荐榜
  • 国产大模型适配优选,MonkeyCode 赋能企业研发
  • 【瑞芯微平台实时Linux方案系列】第二十五篇 - 瑞芯微平台实时Linux驱动开发规范与实践
  • OpenCSG(开放传神)赋能商业银行:三网隔离下的大模型资产跨网治理与复用
  • SSM毕设选题推荐:基于ssm的城市生活e家物业管理平台的设计与开发【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 【录用率高,见刊快,检索稳定 | IEEE Fellow国际化组委】第二届健康信息化与数据分析国际学术会议(HIDA 2026)
  • 计算机SSM毕设实战-基于SSM架构的物业管理系统的设计与实现基于ssm的城市生活e家平台的设计与开发【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 洛轲智能千匠网络:打造跨境B2B商城,启航海外分销新生态 - 圆圆小达人
  • Python之面向对象详解(一篇足矣)