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

枚举实现简单状态机

在 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。

相关推荐
EXI-小洲8 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
2601_961901709 小时前
SpringBoot使用Nacos进行application.yml配置管理
java·spring boot·spring
何以解忧,唯有..10 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
QQ_216962909610 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端
前端 贾公子11 小时前
第09章:上下文与记忆 (2)
java·服务器·前端
2601_9622035111 小时前
Java进阶07集合(续)
java·开发语言
郑州光合科技余经理12 小时前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php
roman_日积跬步-终至千里12 小时前
【数据治理(6)】DataOps 不是调度工具,而是让数据产品长期可信的运行机制
java·服务器·网络
落魄大学生之流水线上谋生计12 小时前
LangGraph 基本用法指南
java·windows·python·langchain
2601_9621284112 小时前
SpringBoot篇(缓存层)
java·spring boot·缓存