状态模式:有限状态机在电商订单系统中的设计与实现

状态模式:有限状态机在电商订单系统中的设计与实现

一、模式核心:用状态切换驱动行为变化

在电商订单系统中,订单状态会随着用户操作动态变化:「已创建」的订单支付后变为「已支付」,发货后变为「已发货」,不同状态下的操作权限和业务逻辑差异巨大。传统方式通过大量if-else判断状态,导致代码臃肿且难以维护。状态模式(State Pattern) 通过将状态封装为独立类,使对象在不同状态下自动切换行为,核心解决:

  • 状态驱动行为:不同状态对应不同操作逻辑,避免海量条件判断
  • 状态转换可控:集中管理状态迁移规则,确保状态变化符合业务流程

核心思想与 UML 类图

img

二、核心实现:构建可扩展的订单状态机

1. 定义状态接口(封装状态相关操作)

public interface OrderState {// 支付操作:不同状态下支付逻辑不同void pay(OrderContext context);// 发货操作:仅特定状态允许发货void deliver(OrderContext context);// 取消操作:不同状态下取消流程不同void cancel(OrderContext context);
}

2. 实现具体状态类(封装各状态的行为)

已创建状态(允许支付和取消)
public class CreatedState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("订单创建状态:执行支付流程...");context.setCurrentState(new PaidState()); // 切换到已支付状态System.out.println("状态变更:已创建 → 已支付");}@Overridepublic void deliver(OrderContext context) {throw new IllegalStateException("错误:未支付订单不能发货");}@Overridepublic void cancel(OrderContext context) {System.out.println("订单创建状态:执行取消流程(无需扣款)");context.setCurrentState(new CanceledState()); // 切换到已取消状态}
}
已支付状态(允许发货和取消)
public class PaidState implements OrderState {@Overridepublic void pay(OrderContext context) {throw new IllegalStateException("错误:订单已支付,请勿重复支付");}@Overridepublic void deliver(OrderContext context) {System.out.println("订单支付状态:执行发货流程...");context.setCurrentState(new DeliveredState()); // 切换到已发货状态System.out.println("状态变更:已支付 → 已发货");}@Overridepublic void cancel(OrderContext context) {System.out.println("订单支付状态:执行取消流程(需退款)");context.setCurrentState(new CanceledState());}
}

3. 上下文类(管理状态切换与状态相关数据)

public class OrderContext {private OrderState currentState;private final String orderId;public OrderContext(String orderId) {this.orderId = orderId;this.currentState = new CreatedState(); // 初始状态为已创建}// 状态切换入口public void setCurrentState(OrderState state) {this.currentState = state;}// 对外暴露的业务操作,委托给当前状态处理public void pay() {currentState.pay(this);}public void deliver() {currentState.deliver(this);}public void cancel() {currentState.cancel(this);}
}

4. 客户端调用示例(状态流转演示)

public class ClientDemo {public static void main(String[] args) {OrderContext order = new OrderContext("ORDER_1001");// 支付操作:创建状态 → 支付状态order.pay(); // 输出:支付流程 & 状态变更// 发货操作:支付状态 → 发货状态order.deliver(); // 输出:发货流程 & 状态变更// 尝试重复支付(已支付状态不允许)try {order.pay();} catch (IllegalStateException e) {System.out.println("异常:" + e.getMessage()); // 输出错误信息}}
}

三、进阶:构建健壮的状态机框架

1. 状态工厂(集中管理状态实例)

public class OrderStateFactory {private static final Map<StateType, OrderState> STATE_POOL = new EnumMap<>(StateType.class);static {STATE_POOL.put(StateType.CREATED, new CreatedState());STATE_POOL.put(StateType.PAID, new PaidState());// 注册所有状态类}public static OrderState getState(StateType type) {return STATE_POOL.get(type);}
}// 使用枚举定义状态类型(避免魔法值)
enum StateType {CREATED, PAID, DELIVERED, CANCELED
}

2. 状态转换校验(防止非法状态迁移)

public abstract class BaseOrderState implements OrderState {// 定义合法的状态转换规则protected abstract Set<StateType> allowedNextStates();@Overridepublic final void transitionTo(OrderContext context, StateType nextState) {if (allowedNextStates().contains(nextState)) {context.setCurrentState(OrderStateFactory.getState(nextState));} else {throw new IllegalArgumentException("非法状态转换:当前状态" + getCurrentState() + "不能转换为" + nextState);}}
}// 具体状态类实现合法转换规则
public class CreatedState extends BaseOrderState {@Overrideprotected Set<StateType> allowedNextStates() {return Set.of(StateType.PAID, StateType.CANCELED); // 仅允许支付或取消}
}

3. 可视化状态机(状态流转图)

支付
取消
发货
取消
确认收货
终止
已创建
已支付
已取消
已发货
已完成
已关闭

四、框架与源码中的状态模式实践

1. Spring State Machine(专业状态机框架)

  • 核心组件:

    • StateMachine:管理状态和转换
    • Transition:定义状态转换条件(如支付成功触发状态变更)
  • 使用示例:

    // 定义订单状态和事件
    StateMachine<OrderState, OrderEvent> stateMachine = StateMachineBuilder.<OrderState, OrderEvent>builder().withStates().initial(OrderState.CREATED).state(OrderState.PAID).end(OrderState.CANCELED, OrderState.COMPLETED).withTransitions().from(OrderState.CREATED).to(OrderState.PAID).on(OrderEvent.PAY).build();stateMachine.sendEvent(OrderEvent.PAY); // 触发状态转换
    

2. MyBatis 事务状态管理

  • Executor接口根据事务状态(自动提交 / 手动提交)切换执行逻辑
  • 通过BaseExecutor的子类(如SimpleExecutorBatchExecutor)实现不同状态下的行为

3. TCP 连接状态(Java NIO 实现)

  • SelectionKey的状态(连接、可读、可写)通过状态模式管理事件分发
  • 避免大量if (key.isReadable())类型的条件判断

五、避坑指南:正确使用状态模式的 3 个要点

1. 避免状态爆炸(控制状态数量)

  • ❌ 反模式:为每个细小状态创建独立类(如订单的「支付中」「发货中」)
  • ✅ 最佳实践:
    • 合并相似状态(如「待审核」「审核中」合并为「审核状态」)
    • 使用状态工厂 + 枚举统一管理状态实例

2. 处理状态转换的原子性

  • 在分布式系统中,状态变更需结合分布式锁或事务保证原子性
// 分布式场景下的状态转换(伪代码)
public void safeTransition(OrderContext context, StateType nextState) {String lockKey = "order_state_lock:" + context.getOrderId();try (RedissonLock lock = redisson.getLock(lockKey)) {lock.lock();currentState.transitionTo(context, nextState);}
}

3. 状态类的职责单一性

  • 状态类应专注于状态相关行为,避免包含业务逻辑之外的代码
  • 复杂业务逻辑可提取为独立服务(如PaymentServiceDeliveryService

六、总结:何时该用状态模式?

适用场景核心特征典型案例
对象状态驱动行为不同状态下操作逻辑差异大,且状态可枚举订单状态机、电梯控制系统、工作流引擎
状态转换规则复杂需要集中管理合法的状态迁移路径游戏角色状态(战斗 / 待机 / 死亡)、设备状态(开机 / 待机 / 关机)
避免海量条件判断拒绝if-else地狱,追求代码可维护性编译器状态(词法分析 / 语法分析 / 语义分析)

状态模式通过「状态封装 + 行为委托」的设计,将状态相关的复杂性从业务逻辑中剥离,使系统在面对状态变化时更具弹性。下一篇我们将深入探讨责任链模式,解析从 Sentinel 流控到审批流程的链式处理逻辑,敬请期待!

扩展思考:状态模式 vs 策略模式

两者都通过「封装变化」实现行为切换,但核心目标不同:

模式变化维度状态关联典型应用
状态模式对象的状态(动态变化)状态之间存在依赖和转换状态机驱动的业务流程
策略模式算法或策略的选择(静态替换)无状态依赖,策略间独立不同排序算法、支付方式选择

理解这种差异,能帮助我们在设计时更精准地选择合适的模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 24.02部署java web服务

ubuntu 24.02 版本推荐使用jdk 21版本部署java web服务&#xff0c;开发后先使用sudo java -jar xxx.jar验证运行结果。 jdk安装&#xff1a;sudo apt install openjdk-21-jdk-headless 编辑服务文本 [Unit] DescriptionWebMgr Java Application Afternetwork.target mysql.…

深入浅出:LDAP 协议全面解析

在网络安全和系统管理的世界中&#xff0c;LDAP&#xff08;轻量级目录访问协议&#xff0c;Lightweight Directory Access Protocol&#xff09;是一个不可忽视的核心技术。它广泛应用于身份管理、认证授权以及目录服务&#xff0c;尤其在企业级环境中占据重要地位。本文将从基…

AI书籍大模型微调-基于亮数据获取垂直数据集

大模型的开源&#xff0c;使得每位小伙伴都能获得AI的加持&#xff0c;包括你可以通过AIGC完成工作总结&#xff0c;图片生成等。这种加持是通用性的&#xff0c;并不会对个人的工作带来定制的影响&#xff0c;因此各个行业都出现了垂直领域大模型。 垂直大模型是如何训练出来…

【2025软考高级架构师】——计算机系统基础(7)

摘要 本文主要介绍了计算机系统的组成&#xff0c;包括硬件和软件两大部分。硬件由处理器、存储器、总线、接口和外部设备等组成&#xff0c;软件则涵盖系统软件和应用软件。文章还详细阐述了冯诺依曼计算机的组成结构&#xff0c;包括 CPU、主存储器、外存等&#xff0c;并解…

AI大模型之模型幻觉

模型幻觉(Model Hallucination)是大模型生成内容时脱离事实、虚构信息的一种现象,尤其在语言模型、图像生成模型等中较为常见。为了更深入地理解这一现象,我们可以将其分为两个维度进行分析:内在幻觉(Intrinsic Hallucination) 和 外在幻觉(Extrinsic Hallucination)。…

spring Ai---向量知识库(一)

在一些垂直领域以及公司内部信息相关或者实时性相关的大模型应用&#xff0c;就无法直接使用chatGPT。 这个时候&#xff0c;向量知识库就进入了。 通过坐标向量最接近的即为匹配相关答案。 向量模型定义&#xff1a;将文档向量化&#xff0c;保证内容越相似的文本&#xff0c;…

Viper配置管理笔记

一、什么是 Viper&#xff1f; Viper 是 Go 语言的一个强大工具&#xff0c;就像一个超级管家&#xff0c;专门负责帮你打理程序的各种配置。它能把配置文件&#xff08;比如 JSON、YAML、TOML 等格式&#xff09;里的内容读出来&#xff0c;还能监控配置文件的变化&#xff0…

实现对象之间的序列化和反序列化

1.什么是序列化&#xff1f; 在项目的开发中&#xff0c;为了让前端更好的分析后端返回的结果&#xff0c;我们一般会将返回的信息进行序列化&#xff0c;序列化就是将返回对象的状态信息转换为一种标准化的格式&#xff0c;方便在网络中传输也方便打印日志时号观察&#xff0…

ThreadLocal - 原理与应用场景详解

ThreadLocal 的基础概念 在 Java 的多线程世界里&#xff0c;线程之间的数据共享与隔离一直是一个关键话题。如果处理不当&#xff0c;很容易引发线程安全问题&#xff0c;比如数据混乱、脏读等。而 ThreadLocal 这个工具类&#xff0c;就像是为线程量身定制的 “私人储物柜”…

iwebsec靶场 文件包含关卡通关笔记11-ssh日志文件包含

目录 日志包含 1.构造恶意ssh登录命令 2.配置ssh日志开启 &#xff08;1&#xff09;配置sshd &#xff08;2&#xff09;配置rsyslog &#xff08;3&#xff09;重启服务 3.写入webshell木马 4.获取php信息渗透 5.蚁剑连接 日志包含 1.构造恶意ssh登录命令 ssh服务…

Diamond软件的使用--(4)搭建Modelsim仿真库

使用Modelsim仿真的原因 由于diamond自带的仿真软件Active-HDL需要另一套Lisence&#xff0c;所以我们使用第三方仿真软件Modelsim来进行仿真。 Modelsim10.5下载链接如下&#xff1a;https://pan.baidu.com/s/1G9699ocWm1UTqK2yS2igyQ 提取码&#xff1a;lewa 一、Lattice仿…

2025年4月19日,四月第三周,C++,字符串数组答案解析

答案与解析 1. 输出字符串数组所有元素 cpp 复制 下载 #include <iostream> using namespace std;int main() {string arr[] = {"apple", "banana", "cherry"};int n = sizeof(arr)/sizeof(arr[0]); // 计算数组长度for (int i = 0; …

C语言之高校学生信息快速查询系统的实现

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之高校学生信息快速查询系统的实现 目录 任务陈述与分析 问题陈述问题分析 数据结构设…

【网络篇】TCP vs UDP底层区别+网络编程概念

大家好呀 我是浪前 今天讲解的是网络篇的第三章&#xff1a;网络编程概念和TCP&UDP的区别 网络编程概念TCP和UDP的区别 跨主机通信:网络编程插座&#xff1a;网络编程的本质&#xff1a; 网络编程的重要概念&#xff1a;客户端和服务器&#xff1a; 客户端和服务器的交互模…

EMIF详解

一、EMIF的基本定义 EMIF&#xff08;External Memory Interface&#xff0c;外部存储器接口&#xff09; 是嵌入式处理器&#xff08;如DSP、FPGA、SoC&#xff09;用于连接外部存储器的专用硬件接口模块&#xff0c;负责管理处理器与存储器之间的地址/数据总线、控制信号及时…

Keil MDK 编译问题:function “HAL_IncTick“ declared implicitly

问题与处理策略 问题描述 ..\..\User\stm32f1xx_it.c(141): warning: #223-D: function "HAL_IncTick" declared implicitlyHAL_IncTick(); ..\..\User\stm32f1xx_it.c: 1 warning, 0 errors问题原因 在 stm32f1xx_it.c 文件中调用了 HAL_IncTick()&#xff0c;但…

Java Web项目(一)

框架 java web项目总工分为两部分&#xff1a;客户端&#xff08;前端&#xff09;和服务端&#xff08;后端&#xff09; 客户端发起请求&#xff0c;服务端接受请求并进行处理 发起请求的方式&#xff1a;from表单、jQuery ajax from表单 造成全局的变化&#xff0c;在发…

Dify部署内网时遇到的代理问题及解决办法

大家知道&#xff0c;在公网环境下利用docker安装dify源码镜像比较容易&#xff0c;详见我之前的文章&#xff0c;基于dify开发agent、workflow等非常方便&#xff0c;本次想着在内部网络环境下也完成部署&#xff0c;以方便更多的人使用&#xff0c;但在部署到内网环境下&…

多节点监控的docker管理面板Portainer安装指南:家庭云计算专家

背景 Portainer 是一个轻量级且功能强大的容器管理面板&#xff0c;专为 Docker 和 Kubernetes 环境设计。它通过直观的 Web 界面简化了容器的部署、管理和监控&#xff0c;即使是非技术用户也能轻松上手。Portainer 支持多节点管理&#xff0c;允许用户从一个中央控制台管理多…

Linux内核哈希表学习笔记

前沿 近期项目中需要给自定义的驱动增加一个功能存储相关的数据信息。结合实际业务层面,最终决定采用哈希表的结构来存储。因为其具备快速查找,插入和删除。其实现原理通过散列函数映射到指定位置。时间复杂度O(1).而且运算速度也快,很适合处理大量的数据场景。但是其也有一…