状态机——枚举实现简单状态机

枚举实现简单状态机

在 Java 中,使用枚举(Enum)来实现状态机(State Machine)是一种非常优雅且高效的做法。Java 的枚举不仅是一个常量的集合,它本质上是一个闭合的类,可以拥有属性、方法,甚至可以实现接口。

最经典、最符合面向对象设计原则的实现方式是:将状态的行为抽象成方法,并在每个枚举常量中分别实现它(状态模式)。

1、业务场景:订单状态流转

我们以一个简单的"订单系统"为例,订单有三个状态:

  • 待付款 (CREATED)
  • 待发货 (PAID)
  • 已收货 (DELIVERED)

触发状态流转的动作(事件)有:

  • pay()(付款)
  • ship()(发货)

2、代码实现

通过在枚举中定义抽象方法,每个状态都可以决定自己在收到某个事件时该如何跳转。

java 复制代码
public enum OrderState {

    // 1. 待付款状态
    CREATED {
        @Override
        public OrderState pay() {
            System.out.println("订单付款成功 -> 准备发货");
            return PAID; // 流转到下一个状态
        }

        @Override
        public OrderState ship() {
            System.out.println("错误:订单尚未付款,不能发货!");
            return this; // 保持原状态
        }
    },

    // 2. 待发货状态
    PAID {
        @Override
        public OrderState pay() {
            System.out.println("提示:订单已付款,请勿重复支付。");
            return this;
        }

        @Override
        public OrderState ship() {
            System.out.println("订单发货成功 -> 商品运输中");
            return DELIVERED; // 流转到下一个状态
        }
    },

    // 3. 已收货状态(终态)
    DELIVERED {
        @Override
        public OrderState pay() {
            System.out.println("错误:订单已结束,无法付款。");
            return this;
        }

        @Override
        public OrderState ship() {
            System.out.println("错误:订单已结束,无法重复发货。");
            return this;
        }
    };

    // 抽象出状态机能接收的事件(动作)
    public abstract OrderState pay();
    public abstract OrderState ship();
}

3、上下文对象(Context)

在实际业务中,我们通常会有一个订单类(Order),它持有了当前的状态,并对外暴露统一的调用接口。

java 复制代码
public class OrderContext {
    private String orderId;
    private OrderState state; // 持有当前状态

    public OrderContext(String orderId) {
        this.orderId = orderId;
        this.state = OrderState.CREATED; // 初始状态为待付款
    }

    // 触发付款动作
    public void payAction() {
        // 将状态变更的控制权交给状态枚举本身
        this.state = this.state.pay();
    }

    // 触发发货动作
    public void shipAction() {
        this.state = this.state.ship();
    }

    public OrderState getState() {
        return state;
    }
}

4、测试运行

java 复制代码
public class StateMachineTest {
    public static void main(String[] args) {
        OrderContext order = new OrderContext("10001");
        System.out.println("当前状态:" + order.getState()); // CREATED

        // 1. 尝试直接发货(非法操作)
        order.shipAction(); // 输出: 错误:订单尚未付款,不能发货!

        // 2. 正常付款
        order.payAction();  // 输出: 订单付款成功 -> 准备发货
        System.out.println("当前状态:" + order.getState()); // PAID

        // 3. 正常发货
        order.shipAction(); // 输出: 订单发货成功 -> 商品运输中
        System.out.println("当前状态:" + order.getState()); // DELIVERED
    }
}
复制代码
当前状态:CREATED
错误:订单尚未付款,不能发货!
订单付款成功 -> 准备发货
当前状态:PAID
订单发货成功 -> 商品运输中
当前状态:DELIVERED

5、总结

优点:

  • 避免了密集的 if-else 或 switch-case
    传统的做法通常是在一个方法里写一大堆 if (status == CREATED) { ... } else if ...。使用枚举将行为内聚到各个状态内部,代码极其清晰。
  • 线程安全与单例保证
    Java 的枚举实例底层是高级单例,JVM 保证了它们的线程安全和全局唯一性。
  • 强类型校验
    编译器会强制要求你流转后的状态必须也是 OrderState 之一,不会出现拼写错误或传入非法状态码的情况。

适用场景:

这种流转逻辑相对固定、状态数量不是特别庞大(比如10个以内)的本地轻量级状态机。如果业务极其复杂且带有大量分布式事务(如跨服务流转),则可以考虑官方的 Spring StateMachine 或阿里开源的 Cola StateMachine。

相关推荐
Rick19936 小时前
【无标题】
java
yoyo_zzm6 小时前
四大编程技术对比:PHP、Java、Python与HTML
java·python·php
海兰6 小时前
【第54篇】Graph + Langfuse 可观测性实战
java·人工智能·spring boot·spring ai
笨拙的老猴子6 小时前
JDK8 / JDK11 / JDK17 / JDK21 核心新特性对比,简单总结
java·jdk
江湖中的阿龙7 小时前
【无标题】
java·开发语言
JavaEdge在掘金7 小时前
06-LangChain Tool 加载与使用指南:预制工具、SerpAPI、edge-tts、GraphQL
java
NettyBoy7 小时前
生产 YoungGC 导致的系统化卡顿
java·jvm