以下是为你精心撰写的 《Java 状态模式深度学习指南》,专为 Java 后端开发者打造,内容涵盖:定义、作用、真实业务场景、实现方式对比、Spring 集成、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目重构、订单系统、审批流程等复杂状态机开发。
📘 Java 状态模式深度学习指南
—— 从“硬编码状态判断”到“对象自我驱动”的架构跃迁
作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / 订单系统 / 审批流)
目标:彻底摆脱“if-else 判断状态”的噩梦,掌握对象行为随状态自动变化的终极设计模式
核心原则:“状态决定行为,行为改变状态” —— 对象自己管理自己的生命周期
✅ 一、什么是状态模式?
📌 定义:
状态模式(State Pattern) 是一种行为型设计模式,它允许一个对象在内部状态改变时改变其行为。对象看起来好像修改了它的类。
🔍 核心思想:
- 将对象的不同状态封装成独立的类(状态类)
- 每个状态类实现相同的行为接口
- 对象(上下文)持有当前状态的引用
- 当状态发生转换时,对象切换内部状态对象,行为随之自动改变
- 状态转换由状态类自己控制,而非外部逻辑判断
💡 一句话记忆:
“我不是在判断状态,我是状态本身。”
🆚 对比策略模式 vs 状态模式:
| 维度 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 客户端选择算法 | 对象内部自动切换行为 |
| 控制权 | 客户端决定用哪个策略 | 对象自己决定下一个状态 |
| 状态是否可变 | 策略是外部注入 | 状态是对象内部属性 |
| 行为变化原因 | 由外部配置触发 | 由内部事件或条件触发 |
| 典型场景 | 支付方式、排序算法 | 订单状态、审批流程、游戏角色 |
✅ 举个生活例子:
你是一个人(上下文),你有三种状态:
学生→ 行为:上课、写作业上班族→ 行为:开会、写PPT退休→ 行为:遛狗、跳广场舞你不是由别人说“你现在是上班族了,该去开会了”,
而是你自己经历了毕业、入职、退休,内在状态变了,行为自然变了。→ 这就是状态模式!
✅ 二、状态模式有什么作用?(Why Use State Pattern?)
| 作用 | 说明 |
|---|---|
| ✅ 消除巨型 if-else 状态判断 | 代码从“状态判断地狱”变成“状态类自治” |
| ✅ 支持开闭原则(OCP) | 新增状态只需新增类,不改原有状态类 |
| ✅ 行为与状态解耦 | 行为逻辑集中在状态类中,上下文只负责调度 |
| ✅ 状态转换逻辑集中 | 状态迁移规则写在状态类内,清晰可维护 |
| ✅ 提高可读性与可测试性 | 每个状态类独立,可单元测试 |
| ✅ 支持复杂状态机 | 多层状态、嵌套状态、状态守卫(Guard)均可实现 |
| ✅ 避免状态污染 | 不同状态拥有独立属性和行为,互不干扰 |
💡 经典名言:
“If you have a class with a lot of conditionals based on state, you probably need the State pattern.”
——《Head First Design Patterns》
✅ 三、状态模式的典型使用场景(Java 后端真实案例)
| 场景 | 说明 | 是否推荐使用状态模式 |
|---|---|---|
| ✅ 订单状态机 | 待支付 → 已支付 → 已发货 → 已完成 → 已取消 | ✅ 强烈推荐 |
| ✅ 合同审批流 | 草稿 → 提交 → 部门审核 → 财务审核 → 已生效 | ✅ 推荐 |
| ✅ 工单系统 | 新建 → 处理中 → 已解决 → 已关闭 | ✅ 推荐 |
| ✅ 用户账户状态 | 激活 → 冻结 → 封禁 → 解封 | ✅ 推荐 |
| ✅ 支付状态 | 待支付 → 支付中 → 支付成功 → 支付失败 | ✅ 推荐 |
| ✅ 任务调度 | 等待执行 → 执行中 → 成功 → 失败 → 重试中 | ✅ 推荐 |
| ✅ 游戏角色状态 | 站立 → 跑步 → 跳跃 → 受伤 → 死亡 | ✅ 推荐 |
| ✅ 微服务状态 | 启动中 → 运行中 → 停止中 → 停止 | ✅ 推荐 |
❌ 简单状态枚举(如 OrderStatus.WAIT_PAY) | 仅用于标识,无行为 | ❌ 用枚举即可 |
| ❌ 无状态变化的业务 | 如“计算总价” | ❌ 用策略模式 |
✅ 判断标准:
“一个对象的行为会随着它的状态改变而显著变化,且状态转换有明确规则?”
→ 是 → 用状态模式!
✅ 四、状态模式的三种实现方式详解(含中文注释)
我们从基础结构到Spring + 状态机引擎,逐步深入。
🔹 1. 基础结构:状态接口 + 多个状态类 + 上下文
/**
* 【1】状态接口:定义所有状态共有的行为契约
*/
interface OrderState {
void handle(OrderContext context); // 处理上下文事件
String getStateName(); // 获取状态名称
}
/**
* 【2】具体状态1:待支付状态
*/
class PendingPaymentState implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("⏳ 当前状态:待支付");
System.out.println("👉 可执行操作:用户支付");
// 仅允许支付操作,其他操作拒绝
context.setNextState(new PaidState()); // 自动切换到“已支付”
}
@Override
public String getStateName() {
return "待支付";
}
}
/**
* 【3】具体状态2:已支付状态
*/
class PaidState implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("✅ 当前状态:已支付");
System.out.println("👉 可执行操作:商家发货");
context.setNextState(new ShippedState()); // 自动切换到“已发货”
}
@Override
public String getStateName() {
return "已支付";
}
}
/**
* 【4】具体状态3:已发货状态
*/
class ShippedState implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("🚚 当前状态:已发货");
System.out.println("👉 可执行操作:用户确认收货");
context.setNextState(new CompletedState()); // 自动切换到“已完成”
}
@Override
public String getStateName() {
return "已发货";
}
}
/**
* 【5】具体状态4:已完成状态
*/
class CompletedState implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("🎉 当前状态:已完成");
System.out.println("⛔ 无法继续操作:订单已完成,不可变更");
// 不做状态转换,保持当前状态
}
@Override
public String getStateName() {
return "已完成";
}
}
/**
* 【6】上下文(Context):持有当前状态,提供统一入口
*/
class OrderContext {
private OrderState currentState; // 当前状态
public OrderContext() {
// 初始化为待支付状态
this.currentState = new PendingPaymentState();
System.out.println("📦 订单已创建,初始状态:" + currentState.getStateName());
}
// 设置下一状态(由当前状态调用)
public void setNextState(OrderState nextState) {
this.currentState = nextState;
System.out.println("🔄 状态已切换至:" + nextState.getStateName());
}
// 客户端调用:触发状态行为
public void triggerEvent() {
System.out.println("=== 触发订单事件 ===");
currentState.handle(this); // 状态自己决定下一步
}
// 获取当前状态
public String getCurrentState() {
return currentState.getStateName();
}
}
/**
* 【7】客户端:使用状态机
*/
public class StateDemo {
public static void main(String[] args) {
OrderContext order = new OrderContext();
// 第一次触发:待支付 → 已支付
order.triggerEvent();
// 第二次触发:已支付 → 已发货
order.triggerEvent();
// 第三次触发:已发货 → 已完成
order.triggerEvent();
// 第四次触发:已完成 → 无变化
order.triggerEvent();
// 输出:
// 📦 订单已创建,初始状态:待支付
// === 触发订单事件 ===
// ⏳ 当前状态:待支付
// 👉 可执行操作:用户支付
// 🔄 状态已切换至:已支付
//
// === 触发订单事件 ===
// ✅ 当前状态:已支付
// 👉 可执行操作:商家发货
// 🔄 状态已切换至:已发货
//
// === 触发订单事件 ===
// 🚚 当前状态:已发货
// 👉 可执行操作:用户确认收货
// 🔄 状态已切换至:已完成
//
// === 触发订单事件 ===
// 🎉 当前状态:已完成
// ⛔ 无法继续操作:订单已完成,不可变更
}
}
✅ 优点:
- 每个状态行为独立,职责清晰
- 状态转换由状态类自己控制,无需外部判断
- 新增状态只需新增类,不修改现有代码
- 符合单一职责原则
⚠️ 缺点:
- 状态类过多时,文件膨胀
- 状态转换逻辑分散在多个类中,不易全局查看
🔹 2. 状态模式 + 枚举(轻量级推荐写法)
适用于状态有限、转换规则固定的场景,代码更简洁
/**
* 【1】订单状态枚举(封装状态行为)
*/
public enum OrderStatus {
PENDING_PAYMENT {
@Override
public OrderStatus next(OrderContext context) {
System.out.println("⏳ 当前状态:待支付");
System.out.println("👉 执行支付操作");
return PAID; // 返回下一状态
}
},
PAID {
@Override
public OrderStatus next(OrderContext context) {
System.out.println("✅ 当前状态:已支付");
System.out.println("👉 执行发货操作");
return SHIPPED;
}
},
SHIPPED {
@Override
public OrderStatus next(OrderContext context) {
System.out.println("🚚 当前状态:已发货");
System.out.println("👉 执行确认收货操作");
return COMPLETED;
}
},
COMPLETED {
@Override
public OrderStatus next(OrderContext context) {
System.out.println("🎉 当前状态:已完成");
System.out.println("⛔ 无法继续操作");
return this; // 保持当前状态
}
};
// 抽象方法:每个状态决定下一状态
public abstract OrderStatus next(OrderContext context);
// 获取状态名称
public String getName() {
return name().replace("_", " ").toLowerCase();
}
}
/**
* 【2】上下文:持有当前状态
*/
class OrderContext {
private OrderStatus status;
public OrderContext() {
this.status = OrderStatus.PENDING_PAYMENT;
System.out.println("📦 订单已创建,初始状态:" + status.getName());
}
// 触发状态流转
public void triggerEvent() {
System.out.println("=== 触发订单事件 ===");
this.status = status.next(this); // 状态自己决定下一步
}
public String getCurrentStatus() {
return status.getName();
}
}
/**
* 【3】客户端
*/
public class EnumStateDemo {
public static void main(String[] args) {
OrderContext order = new OrderContext();
order.triggerEvent(); // 待支付 → 已支付
order.triggerEvent(); // 已支付 → 已发货
order.triggerEvent(); // 已发货 → 已完成
order.triggerEvent(); // 已完成 → 保持
// 输出同上,但代码更简洁!
}
}
✅ 优点:
- 类型安全:编译期检查状态合法性
- 无反射:性能高
- 不可伪造:枚举值有限,防非法状态
- 适合简单状态机
⚠️ 缺点:
- 状态逻辑写在枚举中,复杂逻辑难以维护
- 无法注入 Spring Bean(如数据库服务)
💡 推荐场景:
订单状态、审批流程、游戏状态等状态少、规则固定的场景
🔹 3. 状态模式 + Spring + Spring StateMachine(企业级推荐)
使用 Spring StateMachine 框架,实现可视化状态机、持久化、事件驱动、事务支持
📦 引入依赖(Maven)
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.3.0</version>
</dependency>
✅ 示例:订单状态机(Spring 配置)
/**
* 【1】状态枚举
*/
public enum OrderState {
PENDING_PAYMENT, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
COMPLETED // 已完成
}
/**
* 【2】事件枚举
*/
public enum OrderEvent {
PAY, // 支付事件
SHIP, // 发货事件
CONFIRM // 确认收货事件
}
/**
* 【3】状态机配置类
*/
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
states
.withStates()
.initial(OrderState.PENDING_PAYMENT) // 初始状态
.states(EnumSet.allOf(OrderState.class)); // 所有状态
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
.withExternal()
.source(OrderState.PENDING_PAYMENT).target(OrderState.PAID).event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP)
.and()
.withExternal()
.source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.CONFIRM);
}
}
/**
* 【4】服务层:使用状态机
*/
@Service
public class OrderService {
@Autowired
private StateMachine<OrderState, OrderEvent> stateMachine; // Spring 自动注入
public void processOrder(OrderState initialState) {
// 设置初始状态
stateMachine.getStateMachineAccessor()
.doWithAllRegions(sma -> sma.resetStateMachine(new DefaultStateMachineContext<>(initialState, null, null, null)));
System.out.println("📦 订单初始状态:" + stateMachine.getState().getId());
// 模拟触发事件
sendEvent(OrderEvent.PAY); // 触发支付
sendEvent(OrderEvent.SHIP); // 触发发货
sendEvent(OrderEvent.CONFIRM); // 触发确认
}
private void sendEvent(OrderEvent event) {
if (stateMachine.sendEvent(event)) {
System.out.println("✅ 事件 [" + event + "] 处理成功,当前状态:" + stateMachine.getState().getId());
} else {
System.out.println("❌ 事件 [" + event + "] 无法处理,当前状态:" + stateMachine.getState().getId());
}
}
}
/**
* 【5】启动类测试
*/
@SpringBootApplication
public class SpringStateMachineDemo {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringStateMachineDemo.class, args);
OrderService service = context.getBean(OrderService.class);
service.processOrder(OrderState.PENDING_PAYMENT);
// 输出:
// 📦 订单初始状态:PENDING_PAYMENT
// ✅ 事件 [PAY] 处理成功,当前状态:PAID
// ✅ 事件 [SHIP] 处理成功,当前状态:SHIPPED
// ✅ 事件 [CONFIRM] 处理成功,当前状态:COMPLETED
}
}
✅ 优点:
- 可视化状态图(可生成 DOT 图)
- 事件驱动:异步、重试、监听器支持
- 持久化:状态可存入数据库
- 事务支持:状态变更与业务操作在一个事务中
- 扩展性强:支持守卫条件(Guard)、动作(Action)
💡 企业级推荐:
复杂审批流、订单系统、工作流引擎首选 Spring StateMachine!
🔹 4. 状态模式 + 注解驱动(自定义注解 + AOP 实现)
适用于不想引入外部框架,但仍想实现声明式状态管理
/**
* 【1】自定义注解:标记状态转换规则
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StateTransition {
OrderState from(); // 起始状态
OrderState to(); // 目标状态
OrderEvent event(); // 触发事件
}
/**
* 【2】状态机上下文
*/
@Component
public class AnnotationStateMachine {
private OrderState currentState = OrderState.PENDING_PAYMENT;
@StateTransition(from = OrderState.PENDING_PAYMENT, to = OrderState.PAID, event = OrderEvent.PAY)
public void pay() {
System.out.println("💳 支付成功,状态从【待支付】→【已支付】");
currentState = OrderState.PAID;
}
@StateTransition(from = OrderState.PAID, to = OrderState.SHIPPED, event = OrderEvent.SHIP)
public void ship() {
System.out.println("📦 发货成功,状态从【已支付】→【已发货】");
currentState = OrderState.SHIPPED;
}
@StateTransition(from = OrderState.SHIPPED, to = OrderState.COMPLETED, event = OrderEvent.CONFIRM)
public void confirm() {
System.out.println("✅ 确认收货,状态从【已发货】→【已完成】");
currentState = OrderState.COMPLETED;
}
// 根据事件自动调用对应方法(简化版,实际可用 AOP 实现)
public void trigger(OrderEvent event) {
switch (event) {
case PAY: pay(); break;
case SHIP: ship(); break;
case CONFIRM: confirm(); break;
default: throw new IllegalArgumentException("未知事件:" + event);
}
}
public OrderState getCurrentState() {
return currentState;
}
}
✅ 优点:声明式编程,代码清晰
❌ 缺点:需手动维护映射,不如 Spring StateMachine 强大
✅ 五、状态模式的避坑指南(Java 后端高频踩坑)
| 问题 | 原因 | 解决方案 |
|---|---|---|
❌ 用 if-else 判断状态 | 代码臃肿,违反开闭原则 | ✅ 改为状态模式 |
| ❌ 状态类中调用数据库 | 导致状态类耦合严重 | ✅ 通过上下文注入服务,状态类只调用接口 |
| ❌ 状态转换无校验 | 可能从“已完成”跳到“待支付” | ✅ 在状态类中做守卫判断,或用 Spring StateMachine 的 Guard |
| ❌ 状态枚举不封装行为 | 只当 ID 用 | ✅ 枚举也要实现 next() 行为 |
| ❌ 忘记初始化状态 | 上下文状态为 null | ✅ 构造函数中初始化默认状态 |
| ❌ 状态转换逻辑分散 | 难以全局查看 | ✅ 使用 Spring StateMachine + 状态图可视化 |
| ❌ 状态变更未持久化 | 重启后状态丢失 | ✅ 使用 StateMachinePersister 持久化到数据库 |
✅ 六、状态模式 vs 策略模式 vs 工厂方法 对比
| 模式 | 关注点 | 控制权 | 举例 |
|---|---|---|---|
| 工厂方法 | 创建对象 | 客户端选择工厂 | PaymentFactory.create("alipay") |
| 策略模式 | 执行行为 | 客户端选择策略 | discountStrategy.calculate(price) |
| 状态模式 | 改变行为 | 对象自己决定状态 | order.trigger("pay") → 自动切换状态 |
✅ 三者组合实战:
你用工厂方法创建一个Order对象,
用策略模式选择它的折扣算法,
用状态模式管理它的生命周期(待支付 → 已支付 → 已发货)
→ 三位一体,完美架构!
✅ 七、学习建议与进阶路径
| 阶段 | 建议 |
|---|---|
| 📚 第一周 | 将你项目中的“订单状态判断”重构为状态模式(至少3个状态) |
| 📚 第二周 | 用枚举状态模式重构“用户账户状态”(激活/冻结/封禁) |
| 📚 第三周 | 引入 Spring StateMachine,搭建一个“工单审批流程”状态机 |
| 📚 第四周 | 阅读 Spring StateMachine 源码:StateMachineImpl 如何实现状态流转? |
| 📚 面试准备 | 准备回答: |
“你项目中哪里用了状态模式?”
“状态模式和策略模式区别?”
“Spring StateMachine 和手写状态模式区别?”
“状态模式如何实现状态回滚?” |
✅ 八、状态模式面试高频题(附答案)
Q1:状态模式解决了什么问题?
A:解决了“对象行为随状态变化”导致的
if-else嵌套问题,让对象自己管理状态转换。
Q2:状态模式和策略模式有什么区别?
A:
- 策略模式:客户端主动选择行为(“我要用8折”)
- 状态模式:对象被动响应事件,自动切换状态(“我支付了,所以变成已支付”)
→ 一个是“你选”,一个是“我变”
Q3:状态模式如何实现状态回滚?
A:
- 在状态类中记录上一个状态
- 实现
rollback()方法,切换回上一状态- 或使用 Spring StateMachine 的
restore()+ 持久化功能
Q4:状态模式适合用在哪些场景?
A:订单状态、审批流程、工单系统、游戏状态、设备状态、任务调度等有明确生命周期的场景。
Q5:为什么不用枚举 + if-else 实现状态?
A:枚举可作为状态标识,但无法封装行为,状态转换逻辑分散,不符合“单一职责”和“开闭原则”。
✅ 九、总结:状态模式选型决策树
graph TD
A[是否需要对象行为随状态变化?] --> B{状态是否简单、固定?}
B -->|是| C[使用枚举 + 抽象方法 next()]
B -->|否| D{是否需要持久化、事务、事件监听?}
D -->|是| E[使用 Spring StateMachine]
D -->|否| F[使用接口 + 状态类 + 上下文]
✅ 最终推荐:
- 简单状态(<5个) → ✅ 枚举 + next()(最简洁)
- 复杂状态机(审批流、订单) → ✅ Spring StateMachine(最强大)
- 非 Spring 项目 → ✅ 接口 + 状态类(最标准)
- 绝对不要:
if-else、switch-case、硬编码状态判断
✅ 十、结语:真正的高手,不写 if-else,而是让对象“活”起来
你不是在学“状态模式”,你是在学“如何让对象拥有生命周期”。
✅ 当你看到
order.trigger("pay"),状态自动从“待支付”变成“已支付”时,你已经掌握了面向对象的精髓。
✅ 当你用 Spring StateMachine 管理一个 10+ 状态的审批流,还能回滚、监听、持久化时,你已经是架构师了。
不要为了“用模式”而用模式,
要为了“系统可演化、可维护、可理解”而选择它。
5763

被折叠的 条评论
为什么被折叠?



