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


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

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

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

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

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

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

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

2、代码实现

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

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),它持有了当前的状态,并对外暴露统一的调用接口。

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、测试运行

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值