17、(行为型设计模式)Java 状态模式深度学习指南

以下是为你精心撰写的 《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-elseswitch-case、硬编码状态判断

✅ 十、结语:真正的高手,不写 if-else,而是让对象“活”起来

你不是在学“状态模式”,你是在学“如何让对象拥有生命周期”。

当你看到 order.trigger("pay"),状态自动从“待支付”变成“已支付”时,你已经掌握了面向对象的精髓。
当你用 Spring StateMachine 管理一个 10+ 状态的审批流,还能回滚、监听、持久化时,你已经是架构师了。

不要为了“用模式”而用模式,
要为了“系统可演化、可维护、可理解”而选择它。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值