状态模式
状态模式(State Pattern)是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。通过将状态封装成独立的类,并将行为委托给代表当前状态的对象,从而实现状态与行为的分离。
关键组件:
- 上下文(Context):
- 维护一个指向当前状态的引用,用来表示当前状态
- 提供修改状态的方法,允许状态对象自行切换到其他状态。
- 可向外界提供一些接口,用于触发状态对应的行为。
- 抽象状态类(State):
- 定义一个接口以封装与环境类的一个特定状态相关的行为。
- 封装一些方法处理上下文的请求,这些请求根据对象的状态有不同的实现
- 具体状态类(Concrete State):
- 每个类对应于环境类的一个具体状态,并实现该状态下应有的行为。
- 可以在需要时改变
Context 的状态,由于Context 实例通常作为参数传递给具体状态对象,所以具体状态对象可以根据环境的情况决定转换到其他状态。

状态模式的工作流程
- 请求处理
- Context 接收到请求后,将请求下发给当前状态的对象来处理
- Context 本身不决定如何响应各种请求,所有的决策权都在
Concrete State 中
- 状态转换
- 处理请求的过程中,状态可以判断是否需要转到另一个状态
- 状态转换通常是通过调用Context 提供的方法来实现,这些方法会改变上下文持有的State 对象
- 行为变更
- 一旦状态发生变化,随后的请求就会根据新的状态来处理,从而改变对象的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| interface State { void handleRequest(); }
class ConcreteStateA implements State { public void handleQequest() { System.out.println("State A handling request."); } }
class ConcreteStateB implements State { public void handleRequest() { System.out.println("State B handling request."); } }
class Context { private State currentState;
public Context(State state) { this.currentState = state; }
public void setState(State state) { this.currentState = state; }
public void request() { currentState.handleRequest(); } }
public class Main { public static void main(String[] args) { Context context = new Context(new ConcreteStateA()); context.request(); context.setState(new ConcreteStateB()); context.request(); } }
|
Spring StateMachine
Spring StateMachine 是一个基于 Spring Framework 的库,专门用于管理状态机,并在 Spring 应用中实现状态模式。
主要特性
Spring StateMachine 提供以下主要特性:
- 状态管理:支持状态的定义、状态转换以及状态间的事件触发。
- 事件处理:基于事件驱动,可以响应并处理来自应用程序的各种事件。
- 状态机监听:允许开发者添加监听器来监控状态机的状态变化或事件触发。
- 持久化:支持状态机的持久化,使得状态机即使在应用重启后也能恢复到之前的状态。
- 易于集成:可以轻松地与 Spring 应用集成,利用 Spring 的依赖注入等特性。
使用场景
Spring StateMachine 非常适合处理复杂的业务流程和状态管理问题,例如:
- 订单处理流程(如订单创建、支付、发货、完成)
- 工作流管理
- 游戏逻辑的状态控制
- 设备的状态监控系统
基本概念
在使用 Spring StateMachine 时,主要涉及以下几个概念:
状态(State):
- 系统的某个具体状态,如订单的“已支付”、“未支付”状态。
事件(Event):
- 触发状态转换的行为,如“支付成功”事件可能会将订单状态从“未支付”改变为“已支付”。
转换(Transition):
- 状态之间的迁移规则,定义了在什么事件下,从哪个状态转移到哪个状态。
动作(Action):
- 在状态转换发生时执行的业务逻辑,比如在订单支付成功时发送一封确认邮件。
- 进入动作:进入状态时的执行
- 退出动作:退出状态时的执行
- 转换动作:因事件而发生状态转换的执行
守卫(Guard)
- 评估为真才能进行状态换船的条件,如果不满足条件则不进行转换
Spring StateMachine 为管理复杂的状态逻辑提供了一个结构化和强大的解决方案,它使得状态转换逻辑更加清晰,并且易于维护。通过集成到 Spring 框架中,它也可以利用 Spring 提供的其他功能,如依赖注入和事务管理,从而使开发工作更加便捷。
实践

订单状态
1 2 3 4 5 6 7 8 9 10 11 12
| public enum States { UNPAID, WAITING_FOR_RECEIVE, DONE, CANCELED }
public enum Events { PAY, RECEIVE, CANCEL }
|
配置状态机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| @Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.UNPAID) .state(States.WAITING_FOR_RECEIVE) .end(States.DONE) .end(States.CANCELED); }
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.UNPAID).target(States.WAITING_FOR_RECEIVE).event(Events.PAY) .and() .withExternal() .source(States.WAITING_FOR_RECEIVE).target(States.DONE).event(Events.RECEIVE) .and() .withExternal() .source(States.UNPAID).target(States.CANCELED).event(Events.CANCEL) .and() .withExternal() .source(States.WAITING_FOR_RECEIVE).target(States.CANCELED).event(Events.CANCEL); } @Bean public StateMachineListener<States, Events> listener() { return new StateMachineListenerAdapter<States, Events>() { @Override public void stateChanged(State<States, Events> from, State<States, Events> to) { System.out.println("State changed from " + (from == null ? "none" : from.getId()) + " to " + to.getId()); } }; } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootApplication public class StateMachineTest {
public static void main(String[] args) { SpringApplication app = new SpringApplication(StateMachineTest.class); ApplicationContext context = app.run(args); StateMachine<States, Events> stateMachine = context.getBean(StateMachine.class); stateMachine.start(); stateMachine.sendEvent(Events.PAY); stateMachine.sendEvent(Events.RECEIVE); } }
|
参考