Spring + 设计模式 (十六) 行为型 - 状态模式

状态模式

引言

状态模式是个能让代码在状态切换时"变身"的行为型设计模式。对象根据内部状态改变行为,就像换了个新对象。它的核心是将每个状态的逻辑封装成独立类,上下文通过持有当前状态对象来动态调整行为。相比一堆if-else或switch,状态模式让状态管理更清晰,扩展性更强。想想订单系统:从"待支付"到"已发货",每个状态都有自己的玩法,状态模式让这些切换像翻书一样自然。在企业开发中,它常用于工作流、订单处理、游戏角色等场景,帮你把复杂的状态逻辑理得明明白白。

实际开发中的用途

状态模式在需要管理明确状态和转换规则的场景中特别好使。比如电商订单(待支付 → 已支付 → 已发货),或者任务系统(待处理 → 处理中 → 已完成)。如果用条件语句硬写这些逻辑,状态一多,代码就乱成一团,维护起来跟噩梦似的。状态模式把每个状态的逻辑塞进单独的类,转换规则一目了然,改起来也方便。

举个例子:假设你在搞一个在线考试系统,试卷有"未开始""进行中""已结束"三种状态。未开始时可以改题目,进行中可以交卷,已结束只能看成绩。用状态模式,每个状态是个类,考试对象通过切换状态来改变行为。如果要加个"待批改"状态,新增一个类就行,核心代码几乎不用动,扩展性拉满。

Spring 源码中的应用

状态模式在 Spring 框架里随处可见,Spring StateMachine 是个特别典型的例子。这个框架专为复杂状态转换设计,常用于工作流、订单管理等场景。

Spring StateMachine 的核心类 DefaultStateMachine(引用 Spring StateMachine 2.3.3,路径:org.springframework.statemachine.support.DefaultStateMachine)是状态模式的上下文,负责持有当前状态(State 接口的实现)。状态对象定义了特定状态的行为和转换逻辑。以下是关键代码:

java 复制代码
public class DefaultStateMachine<S, E> extends AbstractStateMachine<S, E> {
    private State<S, E> currentState;

    public DefaultStateMachine(StateMachineContext<S, E> context, State<S, E> initialState) {
        this.currentState = initialState;
    }

    // 处理事件,触发状态切换
    @Override
    public void sendEvent(E event) {
        State<S, E> nextState = currentState.handleEvent(event);
        if (nextState != null) {
            transitionTo(nextState);
        }
    }

    // 切换状态并执行行为
    private void transitionTo(State<S, E> state) {
        this.currentState = state;
        state.entryAction(); // 状态进入时的动作
    }
}

状态模式怎么体现

  • 上下文DefaultStateMachine 持有 currentState,负责状态管理。
  • 状态接口State<S, E> 定义了状态的行为,比如 handleEvent 处理事件。
  • 动态切换transitionTo 切换状态,并调用新状态的 entryAction
  • 解耦 :每个状态(如 ChoiceState)独立实现逻辑,转换规则清晰。

解决了啥:Spring StateMachine 用状态模式把状态转换逻辑从业务代码里抽出来,开发者只管定义状态和事件,框架自动处理切换。这让复杂流程的实现简单多了,维护成本也低。

Java 代码案例

下面用 Spring Boot 和 Spring StateMachine 写个电商订单状态管理的案例,展示状态模式在实际项目里的用法。订单有"待支付""已支付""已发货""已完成"四种状态,通过状态机管理转换。

代码实现

java 复制代码
// 订单状态
public enum OrderState {
    PENDING_PAYMENT, PAID, SHIPPED, COMPLETED
}

// 订单事件
public enum OrderEvent {
    PAY, SHIP, COMPLETE, CANCEL
}

// 状态机配置
@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.COMPLETE)
                .and()
            .withExternal()
                .source(OrderState.PENDING_PAYMENT).target(OrderState.PENDING_PAYMENT).event(OrderEvent.CANCEL);
    }

    // 状态切换日志
    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderState, OrderEvent> config) throws Exception {
        config
            .withConfiguration()
            .listener(new StateMachineListenerAdapter<OrderState, OrderEvent>() {
                @Override
                public void stateChanged(State<OrderState, OrderEvent> from, State<OrderState, OrderEvent> to) {
                    System.out.println("Order state changed to: " + to.getId());
                }
            });
    }
}

// 订单服务
@Service
public class OrderService {

    @Autowired
    private StateMachine<OrderState, OrderEvent> stateMachine;

    public void sendEvent(OrderEvent event) {
        stateMachine.start();
        stateMachine.sendEvent(event);
    }

    public OrderState getCurrentState() {
        return stateMachine.getState().getId();
    }
}

代码说明

  • 状态模式体现StateMachine 是上下文,OrderState 定义状态,OrderEvent 触发转换。转换规则在 OrderStateMachineConfig 里配置,状态逻辑完全解耦。
  • 解决的问题:订单状态管理通过配置实现,业务代码只管发送事件,状态机搞定切换逻辑。加新状态或事件只改配置,代码几乎不用动。
  • 好处:代码清晰,状态逻辑集中,测试和维护都方便。Spring StateMachine 还支持持久化、事件监听等,适合复杂场景。
  • 怎么跑 :通过 OrderServicesendEvent 触发事件,比如 PAY 事件让订单从 PENDING_PAYMENT 变成 PAID,日志会记录状态变化。

总结

状态模式是个管理状态转换的狠角色,它把状态逻辑装进独立类,干掉乱七八糟的条件分支,让代码清爽又好改。在 Spring StateMachine 里,状态模式被玩得炉火纯青,DefaultStateMachine 通过切换状态对象,把复杂的状态管理变成配置游戏,订单、工作流这些场景直接起飞。我们的订单案例也证明了它的实力:配置化的状态机让业务逻辑简单明了,扩展性强,维护起来省心。

(对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢

相关推荐
our_times1 小时前
2026年Java开发者破局指南:Spring AI 2.0 与 Agent 开发实战
java·人工智能·spring
杨充1 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充2 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充2 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充2 小时前
2.面向对象的特性
设计模式
杨充2 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充2 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充2 小时前
3.接口vs抽象类比较
设计模式
wear工程师2 小时前
@Transactional 标了却没开事务?先看这次调用有没有经过代理
java·spring