news 2026/4/15 13:08:59

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 框架(企业级状态机库)进一步简化。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 23:24:01

SSM计算机毕设之基于ssm的城市生活e家平台的设计与开发在线报修与维修反馈 在线评价(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/9 23:30:46

Java面试通关指南(六):数据库王者之战:MySQL深度优化与分布式实践

&#x1f525; 前言 在互联网企业的技术面试中&#xff0c;MySQL是必考的重中之重。掌握MySQL不仅是基础&#xff0c;更是区分普通开发者与高级工程师的关键。本文将带你深入MySQL内核&#xff0c;探索从单机优化到分布式架构的完整知识体系。 一、索引背后的B树秘密 面试高…

作者头像 李华
网站建设 2026/4/4 8:46:07

P0904AK桌面接口模块

P0904AK 桌面接口模块产品特点开头&#xff1a; P0904AK 桌面接口模块是一款用于工业自动化控制系统的接口扩展单元&#xff0c;主要提供现场设备与主控系统之间的便捷连接与互动接口&#xff0c;旨在简化设备接入、提高系统集成效率。产品特点&#xff1a;提供多种常用接口类型…

作者头像 李华
网站建设 2026/4/11 3:33:24

中国人民大学在迭代13个版本后推出的大模型书籍,简直不要太详细,太适合入门大模型了(附PDF)

中国人大在收录900余篇参考文献&#xff0c;迭代13个版本后&#xff0c;终于出版了这本大模型中文版&#xff01; 与英文综述文章定位不同&#xff0c;这本书更注重为初学者提供关于大模型技术的全面了解&#xff0c;是专门为深度学习基础的高年级本科生以及低年级研究生使用&a…

作者头像 李华
网站建设 2026/4/15 11:23:22

科技成果转化新引擎——智能顾问赋能全链条服务生态

在当今创新驱动发展的时代&#xff0c;科技成果的转化效率和质量直接关系到国家竞争力和经济发展潜力。然而&#xff0c;传统成果转化模式存在诸多痛点&#xff1a;技术评估门槛高、市场需求匹配不精准、成果推广能力弱以及筛选与资源对接依赖人工等问题&#xff0c;这些问题严…

作者头像 李华
网站建设 2026/4/12 13:25:59

STM32晶振频率怎么选

一、高速晶振&#xff08;HSE&#xff09;&#xff1a;8MHz是“官方推荐”主流选择&#xff1a;STM32官方例程、开发板默认用8MHz&#xff0c;通过内部PLL倍频到72MHz&#xff08;如STM32F1系列&#xff09;。为啥选它&#xff1a;兼容性最好&#xff0c;资料多&#xff0c;出问…

作者头像 李华