状态机——状态模式实现订单状态流转

状态模式实现订单状态流转

1、问题概述

在状态模式中,我们将每一个状态都封装成一个类。状态的流转不再是一堆恶心的 if-else 或 switch-case,而是让状态对象自己去决定下一个状态是谁。

2、代码实现

2.1、定义环境上下文(OrderContext)

上下文负责维护当前的订单状态,并暴露出触发事件的接口。

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

    public OrderContext(Long orderId) {
        this.orderId = orderId;
        // 初始状态:对应图中的"开始" -> "待付款"
        this.state = new PendingPayState();
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public OrderState getState() {
        return this.state;
    }

    // ========== 暴露给外部业务的事件方法 ==========

    //用户支付
    public void userPay() {
        state.userPay(this);
    }

    //商家发货
    public void merchantDeliver() {
        state.merchantDeliver(this);
    }

    //收到包裹
    public void receivePackage() {
        state.receivePackage(this);
    }

    //评价
    public void comment() {
        state.comment(this);
    }

    //超时未评价
    public void commentTimeout() {
        state.commentTimeout(this);
    }

    //手动取消支付
    public void manualCancel() {
        state.manualCancel(this);
    }

    //超时未支付
    public void payTimeout() {
        state.payTimeout(this);
    }
}

2.2、抽象状态接口(OrderState)

定义所有状态的共同接口,包含图中所有可能发生的动作/事件。这里使用抽象类,并为所有方法提供默认实现(默认抛出异常,意味着当前状态不允许这个操作)。

java 复制代码
public abstract class OrderState {

    // 获取当前状态名称(方便打日志或存库)
    public abstract String getStateName();

    public void userPay(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [用户支付]");
    }

    public void merchantDeliver(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [商家发货]");
    }

    public void receivePackage(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [收到包裹]");
    }

    public void comment(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [评价]");
    }

    public void commentTimeout(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [超时未评]");
    }

    public void manualCancel(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [手动取消]");
    }

    public void payTimeout(OrderContext context) {
        throw new UnsupportedOperationException(getStateName() + " 状态下无法执行 [超时未支付]");
    }
}

2.3、具体状态类实现

每个状态类只需要重写自己关心的事件。未重写的方法会直接继承父类的抛错,天然做到了状态流转的安全校验。

  1. 待付款状态(PendingPayState)
java 复制代码
public class PendingPayState extends OrderState {
    @Override
    public String getStateName() { return "待付款"; }

    @Override
    public void userPay(OrderContext context) {
        System.out.println("【业务逻辑】核心支付成功,扣减真实库存...");
        // 流转到:待发货
        context.setState(new PendingDeliverState());
    }

    @Override
    public void manualCancel(OrderContext context) {
        System.out.println("【业务逻辑】用户手动取消订单,释放预扣库存...");
        context.setState(new CancelledState());
    }

    @Override
    public void payTimeout(OrderContext context) {
        System.out.println("【业务逻辑】订单支付超时,系统自动取消...");
        context.setState(new CancelledState());
    }
}
  1. 待发货状态(PendingDeliverState)
java 复制代码
public class PendingDeliverState extends OrderState {
    @Override
    public String getStateName() { return "待发货"; }

    @Override
    public void merchantDeliver(OrderContext context) {
        System.out.println("【业务逻辑】商家绑定物流单号,发送出库通知...");
        // 流转到:待收货
        context.setState(new PendingReceiveState());
    }
}
  1. 待收货状态(PendingReceiveState)
java 复制代码
public class PendingReceiveState extends OrderState {
    @Override
    public String getStateName() { return "待收货"; }

    @Override
    public void receivePackage(OrderContext context) {
        System.out.println("【业务逻辑】确认收货成功,资金结算给商家...");
        // 流转到:待评价
        context.setState(new PendingCommentState());
    }
}
  1. 待评价状态(PendingCommentState)
java 复制代码
public class PendingCommentState extends OrderState {
    @Override
    public String getStateName() { return "待评价"; }

    @Override
    public void comment(OrderContext context) {
        System.out.println("【业务逻辑】写入用户评价数据,发放积分奖励...");
        context.setState(new CompletedState());
    }

    @Override
    public void commentTimeout(OrderContext context) {
        System.out.println("【业务逻辑】系统超时未评,默认好评...");
        context.setState(new CompletedState());
    }
}

5. 终态:已取消(CancelledState)与 已完成(CompletedState)

这两个是图中的终点,它们不重写任何流转方法(即任何后续操作都会报错)。

java 复制代码
public class CancelledState extends OrderState {
    @Override
    public String getStateName() { return "已取消(终态)"; }
}

public class CompletedState extends OrderState {
    @Override
    public String getStateName() { return "已完成(终态)"; }
}

3、测试

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 1. 初始化订单(自动进入 待付款)
        OrderContext order = new OrderContext(10086L);
        System.out.println("当前订单状态:" + order.getState().getStateName());

        // 2. 正常流转:支付 -> 发货 -> 收货 -> 评价
        order.userPay();
        System.out.println("当前订单状态:" + order.getState().getStateName());

        order.merchantDeliver();
        System.out.println("当前订单状态:" + order.getState().getStateName());

        order.receivePackage();
        System.out.println("当前订单状态:" + order.getState().getStateName());

        order.comment();
        System.out.println("当前订单状态:" + order.getState().getStateName());

        System.out.println("---------------------------------------");

        // 3. 测试异常流转(比如在 [已完成] 状态下,企图去调用 [手动取消])
        try {
            order.manualCancel();
        } catch (UnsupportedOperationException e) {
            System.err.println("拦截非法操作成功: " + e.getMessage());
        }
    }
}
java 复制代码
当前订单状态:待付款
【业务逻辑】核心支付成功,扣减真实库存...
当前订单状态:待发货
【业务逻辑】商家绑定物流单号,发送出库通知...
当前订单状态:待收货
【业务逻辑】确认收货成功,资金结算给商家...
当前订单状态:待评价
【业务逻辑】写入用户评价数据,发放积分奖励...
当前订单状态:已完成(终态)
---------------------------------------
拦截非法操作成功: 已完成(终态) 状态下无法执行 [手动取消]
相关推荐
小马不起床13 小时前
零基础玩转 FastAPI_09:前端基础工程构建
前端·状态模式·fastapi
希洛兔18 小时前
状态模式详解:让对象随内部状态自动改变行为
java·设计模式·状态模式
星恒随风1 天前
Linux进程间通信(二):深入理解System V共享内存、消息队列、信号量与内核IPC资源管理
linux·运维·服务器·笔记·学习·状态模式
星恒随风1 天前
Linux进程间通信(一):从匿名管道、命名管道到进程池,深入理解Pipe、FIFO与文件描述符
linux·运维·笔记·学习·状态模式
星恒随风2 天前
Linux库的底层原理:ELF文件格式、静态链接、程序加载、虚拟地址空间与GOT/PLT详解
linux·运维·笔记·学习·状态模式
今年下半年3 天前
【低成本埋点统计】Spring Boot+vue2前后端分离项目统计系统访问、使用、登录次数
spring boot·后端·状态模式
AZaLEan__3 天前
Mock与useMock
状态模式
星恒随风4 天前
Linux基础IO万字详解:从文件描述符fd到重定向、FILE缓冲机制及MiniShell升级
linux·运维·笔记·学习·状态模式
VidDown5 天前
上传 2GB 文件别让 Django 接:分片上传、对象存储直传与秒传
python·django·音视频·状态模式·实时音视频·视频编解码·视频
Lvan的前端笔记5 天前
docker:基于docker的前端部署方案
前端·docker·状态模式