2. 状态机与业务逻辑分离
任务目标
在订单处理系统中,状态转换逻辑与业务操作(如发送邮件、扣减库存)经常耦合在同一个方法中,导致状态机难以维护和复用。本案例的目标是将状态转换规则与业务操作分离:使用配置表驱动状态转换,业务操作通过监听器机制在状态变更时触发,从而在不修改状态机核心代码的前提下扩展业务行为。
核心设计
采用两个核心组件:状态转换配置表(声明式定义合法转换路径)和状态监听器接口(业务操作的挂载点)。状态机引擎只负责校验和推进状态,不包含任何具体业务逻辑。
完整实现
import java.util.*;
// 1. 状态与事件定义
enum OrderState {
CREATED, PAID, SHIPPED, COMPLETED, CANCELLED
}
enum OrderEvent {
PAY, SHIP, COMPLETE, CANCEL
}
// 2. 状态转换配置表(核心分离点)
class StateTransitionConfig {
// key: 当前状态 + 事件 -> value: 目标状态
private static final Map<String, OrderState> TRANSITIONS = new HashMap<>() {{
put(key(OrderState.CREATED, OrderEvent.PAY), OrderState.PAID);
put(key(OrderState.CREATED, OrderEvent.CANCEL), OrderState.CANCELLED);
put(key(OrderState.PAID, OrderEvent.SHIP), OrderState.SHIPPED);
put(key(OrderState.PAID, OrderEvent.CANCEL), OrderState.CANCELLED);
put(key(OrderState.SHIPPED, OrderEvent.COMPLETE), OrderState.COMPLETED);
}};
private static String key(OrderState state, OrderEvent event) {
return state.name() + ":" + event.name();
}
static Optional<OrderState> getNextState(OrderState current, OrderEvent event) {
return Optional.ofNullable(TRANSITIONS.get(key(current, event)));
}
}
// 3. 业务操作监听器接口(业务逻辑挂载点)
interface OrderStateListener {
void onStateChanged(Order order, OrderState oldState, OrderState newState);
}
// 4. 订单实体
class Order {
private String id;
private OrderState state;
private final List<OrderStateListener> listeners = new ArrayList<>();
public Order(String id) {
this.id = id;
this.state = OrderState.CREATED;
}
public void addListener(OrderStateListener listener) {
listeners.add(listener);
}
public OrderState getState() {
return state;
}
public String getId() {
return id;
}
// 5. 状态机引擎:只做转换校验与状态更新,不包含业务逻辑
public boolean fireEvent(OrderEvent event) {
Optional<OrderState> nextState = StateTransitionConfig.getNextState(state, event);
if (nextState.isEmpty()) {
return false; // 非法转换
}
OrderState oldState = state;
state = nextState.get();
// 通知所有监听器执行业务操作
for (OrderStateListener listener : listeners) {
listener.onStateChanged(this, oldState, state);
}
return true;
}
}
// 6. 业务操作实现(与状态机完全解耦)
class OrderBusinessHandlers {
// 业务操作1:支付后发送确认邮件
static OrderStateListener paymentNotification = (order, oldState, newState) -> {
if (oldState == OrderState.CREATED && newState == OrderState.PAID) {
System.out.println("[邮件] 订单 " + order.getId() + " 支付成功通知已发送");
}
};
// 业务操作2:发货后更新物流
static OrderStateListener shippingHandler = (order, oldState, newState) -> {
if (oldState == OrderState.PAID && newState == OrderState.SHIPPED) {
System.out.println("[物流] 订单 " + order.getId() + " 已分配快递单号");
}
};
// 业务操作3:完成订单后触发评价邀请
static OrderStateListener completionHandler = (order, oldState, newState) -> {
if (oldState == OrderState.SHIPPED && newState == OrderState.COMPLETED) {
System.out.println("[评价] 订单 " + order.getId() + " 已发送评价邀请");
}
};
}
// 7. 最小可运行入口
public class OrderStateMachineDemo {
public static void main(String[] args) {
Order order = new Order("ORD-2024-001");
// 注册业务监听器(可在运行时动态增删)
order.addListener(OrderBusinessHandlers.paymentNotification);
order.addListener(OrderBusinessHandlers.shippingHandler);
order.addListener(OrderBusinessHandlers.completionHandler);
// 执行状态流转
System.out.println("初始状态: " + order.getState());
System.out.println("触发支付: " + order.fireEvent(OrderEvent.PAY));
System.out.println("当前状态: " + order.getState());
System.out.println("触发发货: " + order.fireEvent(OrderEvent.SHIP));
System.out.println("当前状态: " + order.getState());
System.out.println("触发完成: " + order.fireEvent(OrderEvent.COMPLETE));
System.out.println("当前状态: " + order.getState());
// 验证非法转换被拒绝
System.out.println("\n尝试从COMPLETED触发支付(非法): " + order.fireEvent(OrderEvent.PAY));
System.out.println("状态未改变: " + order.getState());
}
}
运行方式
# 编译并运行(无需第三方依赖)
javac OrderStateMachineDemo.java
java OrderStateMachineDemo
验证方法
程序输出应满足以下条件,即可判定实现正确:
- 状态按
CREATED → PAID → SHIPPED → COMPLETED顺序正确流转,每次fireEvent返回true; - 每次状态变更后,对应监听器输出业务提示(邮件、物流、评价各一条);
- 对
COMPLETED状态触发PAY事件时返回false,且状态保持COMPLETED不变,证明非法转换被配置表拦截。
预期输出
初始状态: CREATED
触发支付: true
[邮件] 订单 ORD-2024-001 支付成功通知已发送
当前状态: PAID
触发发货: true
[物流] 订单 ORD-2024-001 已分配快递单号
当前状态: SHIPPED
触发完成: true
[评价] 订单 ORD-2024-001 已发送评价邀请
当前状态: COMPLETED
尝试从COMPLETED触发支付(非法): false
状态未改变: COMPLETED
关键要点
- 配置驱动:所有合法转换路径集中在
StateTransitionConfig的TRANSITIONS映射中,新增状态或事件只需修改此表,无需改动引擎代码。 - 业务解耦:业务操作通过
OrderStateListener接口注入,状态机引擎不感知具体业务。新增业务(如短信通知)只需新增一个监听器实现并注册,符合开闭原则。 - 非法转换防护:未在配置表中的状态-事件组合会被
Optional.empty()拦截,返回false,保证状态机状态合法性由配置统一约束。