Java 状态机详解:三种实现方式消除 if-else 嵌套
Java 状态机通过状态模式消除复杂的 if-else 嵌套。文章介绍了三种实现方式:经典状态模式定义接口与上下文,枚举结合方法返回新状态实现轻量级状态机,以及使用 Spring StateMachine 框架进行配置化开发。对比了各方案的优缺点及适用场景,帮助开发者根据业务复杂度选择合适的状态管理方案。

Java 状态机通过状态模式消除复杂的 if-else 嵌套。文章介绍了三种实现方式:经典状态模式定义接口与上下文,枚举结合方法返回新状态实现轻量级状态机,以及使用 Spring StateMachine 框架进行配置化开发。对比了各方案的优缺点及适用场景,帮助开发者根据业务复杂度选择合适的状态管理方案。

在之前的设计模式专栏中,介绍过一个状态模式(津津乐道设计模式 - 状态模式详解),并以交通信号灯的实现进行状态模式的演示讲解,实际上状态模式也特别适合处理那些包含大量条件语句(如 if-else 或 switch)的复杂状态转换逻辑。

为什么使用状态模式消除 if-else?
在日常 Java 开发中,我们常常遇到这样的情况:
这时,如果我们用 if-else 或 switch-case 来写,代码很容易变得臃肿难维护:比如下面代码
if(status == 0){ // 待支付逻辑
} else if(status == 1){ // 已支付逻辑
} else if(status == 2){ // 已发货逻辑
} else if(status == 3){ // 已收货逻辑
}
这种写法的问题: 逻辑分支多,扩展性差 新增状态时,需要修改原有代码(违反开闭原则) 状态切换逻辑容易分散在各个 if-else 中,不够集中
那么使用状态机(State Machine)也可称状态模式,就可以解决上述这些问题,并具备以下优点:
假设有一个电商订单系统,订单状态有:
订单可以: 从待支付 → 支付 → 发货 → 完成 取消订单(在待支付状态下)
那么可能你的代码就是像这样的:
public class OrderServiceIfElse {
public void handle(int status, String action) {
if (status == 0) {
if ("pay".equals(action)) {
System.out.println("订单已支付");
} else if ("cancel".equals(action)) {
System.out.println("订单已取消");
}
} else if (status == 1) {
if ("ship".equals(action)) {
System.out.println("订单已发货");
}
} else if (status == 2) {
if ("confirm".equals(action)) {
System.out.println("订单已完成");
}
}
}
}
又或者是这样的:
public class Order {
private String state;
public void process() {
if ("NEW".equals(state)) {
System.out.println("处理新订单");
state = "PROCESSING";
} else if ("PROCESSING".equals(state)) {
System.out.println("订单处理中");
state = "SHIPPED";
} else if ("SHIPPED".equals(state)) {
System.out.println("订单已发货");
state = "DELIVERED";
} else if ("DELIVERED".equals(state)) {
System.out.println("订单已完成");
} else {
throw new IllegalStateException("无效订单状态:" + state);
}
}
}
无论是哪一种写法,都会存在以下问题: 当状态增多时,代码变得臃肿难以维护 违反开闭原则,添加新状态需要修改现有代码 状态转换逻辑分散在多个地方 难以跟踪状态转换规则
下面我们使用状态模式重构上述订单处理流程:
public interface OrderState {
void pay(OrderContext context);
void ship(OrderContext context);
void confirm(OrderContext context);
void cancel(OrderContext context);
}
public class OrderContext {
private OrderState state;
public OrderContext(OrderState state) {
this.state = state;
}
public void setState(OrderState state) {
this.state = state;
}
public void pay() { state.pay(this); }
public void ship() { state.ship(this); }
public void confirm() { state.confirm(this); }
public void cancel() { state.cancel(this); }
}
// 待支付
public class PendingPayState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("订单支付成功");
context.setState(new PaidState());
}
@Override
public void ship(OrderContext context) {
System.out.println("请先支付订单");
}
@Override
public void confirm(OrderContext context) {
System.out.println("请先支付订单");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消");
}
}
// 已支付
public class PaidState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("订单已支付,请勿重复支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("订单已发货");
context.setState( ());
}
{
System.out.println();
}
{
System.out.println();
}
}
{
{
System.out.println();
}
{
System.out.println();
}
{
System.out.println();
context.setState( ());
}
{
System.out.println();
}
}
{
{
System.out.println();
}
{
System.out.println();
}
{
System.out.println();
}
{
System.out.println();
}
}
public class StateMachineTest {
public static void main(String[] args) {
// 初始状态:待支付
OrderContext order = new OrderContext(new PendingPayState());
order.pay(); // 支付
order.ship(); // 发货
order.confirm(); // 确认收货
// 分别输出
// 订单支付成功
// 订单已发货
// 订单已完成
}
}
这里再升级一下使用枚举 + Map 的轻量状态机实现,这种写法相比经典状态模式,代码量更少,适合状态数不多、且状态逻辑相对简单的场景。
这种写法的核心思想是:
用枚举类定义所有状态 每个状态实现自己的行为逻辑 通过 Map 注册状态与处理逻辑的映射关系 直接通过当前状态对象来执行对应方法,避免 if-else
public enum OrderStateEnum {
PENDING_PAY {
@Override
public OrderStateEnum pay() {
System.out.println("订单支付成功");
return PAID;
}
@Override
public OrderStateEnum ship() {
System.out.println("请先支付订单");
return this;
}
@Override
public OrderStateEnum confirm() {
System.out.println("请先支付订单");
return this;
}
@Override
public OrderStateEnum cancel() {
System.out.println("订单已取消");
return this;
}
},
PAID {
@Override
public OrderStateEnum pay() {
System.out.println("订单已支付,请勿重复支付");
return this;
}
@Override
public OrderStateEnum ship() {
System.out.println("订单已发货");
return SHIPPED;
}
@Override
public OrderStateEnum confirm() {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
;
}
},
SHIPPED {
OrderStateEnum {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
COMPLETED;
}
OrderStateEnum {
System.out.println();
;
}
},
COMPLETED {
OrderStateEnum {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
;
}
OrderStateEnum {
System.out.println();
;
}
};
OrderStateEnum ;
OrderStateEnum ;
OrderStateEnum ;
OrderStateEnum ;
}
这里的设计是:
每个状态用一个枚举常量表示 每个枚举常量实现自己的状态切换逻辑 方法返回新的状态枚举,实现状态流转
public class OrderContextEnum {
private OrderStateEnum state;
public OrderContextEnum(OrderStateEnum state) {
this.state = state;
}
public void pay() { state = state.pay(); }
public void ship() { state = state.ship(); }
public void confirm() { state = state.confirm(); }
public void cancel() { state = state.cancel(); }
}
public class EnumStateMachineTest {
public static void main(String[] args) {
OrderContextEnum order = new OrderContextEnum(OrderStateEnum.PENDING_PAY);
order.pay(); // 支付
order.ship(); // 发货
order.confirm(); // 确认收货
}
}
Spring StateMachine 是 Spring 官方提供的状态机框架,支持可配置状态流转、事件驱动、监听器回调等功能,特别适合业务流程复杂、状态多变的场景。
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.2.1</version>
</dependency>
// 订单状态
public enum OrderState {
PENDING_PAY, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
COMPLETED // 已完成
}
// 触发事件
public enum OrderEvent {
PAY, // 支付
SHIP, // 发货
CONFIRM, // 确认收货
CANCEL // 取消订单
}
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import java.util.EnumSet;
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
states.withStates()
.initial(OrderState.PENDING_PAY) // 初始状态
.states(EnumSet.allOf(OrderState.class)); // 所有状态
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions.withExternal()
.source(OrderState.PENDING_PAY)
.target(OrderState.PAID)
.event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderState.PAID)
.target(OrderState.SHIPPED)
.event(OrderEvent.SHIP)
.and()
.withExternal()
.source(OrderState.SHIPPED)
.target(OrderState.COMPLETED)
.event(OrderEvent.CONFIRM)
.and()
.withExternal()
.source(OrderState.PENDING_PAY)
.target(OrderState.PENDING_PAY)
.event(OrderEvent.CANCEL);
}
Exception {
config.withConfiguration()
.listener(newStateMachineListenerAdapter<>() {
{
System.out.println( + (from == ? : from.getId()) + + to.getId());
}
});
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Component;
@Component
public class OrderStateMachineTest implements CommandLineRunner {
@Autowired
private StateMachine<OrderState, OrderEvent> stateMachine;
@Override
public void run(String... args) throws Exception {
stateMachine.start();
stateMachine.sendEvent(OrderEvent.PAY);
stateMachine.sendEvent(OrderEvent.SHIP);
stateMachine.sendEvent(OrderEvent.CONFIRM);
stateMachine.stop();
}
}
观察控制台会输出如下:
状态切换:无 -> PENDING_PAY 状态切换:PENDING_PAY -> PAID 状态切换:PAID -> SHIPPED 状态切换:SHIPPED -> COMPLETED
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 经典状态模式 | 结构清晰,面向对象 | 类文件多,状态多时管理复杂 | 状态多、逻辑复杂的 OO 场景 |
| 枚举状态机 | 简洁,集中管理 | 状态多时枚举类太长 | 状态少、逻辑简单 |
| Spring StateMachine | 功能强大,可配置化 | 需要额外依赖,学习成本高 | 大型系统、状态规则经常变动 |
经典状态模式适合复杂业务流程,易于模块化管理 枚举 + Map 注册或枚举直接实现行为,适合小型项目或简单状态流转 在 Java 中,枚举天生是单例的,用它实现状态机既简洁又线程安全
本文详细介绍了使用状态模式消除 if-else,通过经典状态模式、枚举状态机、Spring StateMachine 三种方式,从纯手写模式 → 枚举模式 → 框架模式的完整对比,进行了相关代码演示。当发现自己在编写大量条件语句来处理对象状态时,考虑使用状态模式重构您的代码。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online