【设计模式手册019】状态模式 - 管理对象状态转换
本文是「设计模式手册」系列第019篇,我将以深入浅出、追本溯源的风格,带你真正理解状态模式的精髓。
1. 我们为何需要状态模式?
在软件设计中,我们经常会遇到这样的场景:对象的行为取决于它的状态,并且需要在运行时根据状态改变行为。比如:
- 订单系统:待支付、已支付、已发货、已完成、已取消
- 电梯控制:停止、运行、开门、关门、故障
- 游戏角色:正常、中毒、眩晕、死亡、无敌
- 工作流引擎:草稿、审批中、已批准、已拒绝
初级程序员的写法:
java
public class Order {
private String state;
public void process() {
if ("PENDING_PAYMENT".equals(state)) {
// 待支付状态的处理逻辑
System.out.println("处理支付...");
state = "PAID";
} else if ("PAID".equals(state)) {
// 已支付状态的处理逻辑
System.out.println("准备发货...");
state = "SHIPPED";
} else if ("SHIPPED".equals(state)) {
// 已发货状态的处理逻辑
System.out.println("等待确认收货...");
state = "COMPLETED";
} else if ("COMPLETED".equals(state)) {
// 已完成状态的处理逻辑
System.out.println("订单已完成,无法继续处理");
} else if ("CANCELLED".equals(state)) {
// 已取消状态的处理逻辑
System.out.println("订单已取消,无法处理");
}
// 每新增一个状态,这里就要加一个else if
}
public void cancel() {
if ("PENDING_PAYMENT".equals(state)) {
// 待支付状态的取消逻辑
System.out.println("取消待支付订单...");
state = "CANCELLED";
} else if ("PAID".equals(state)) {
// 已支付状态的取消逻辑
System.out.println("取消已支付订单,需要退款...");
state = "CANCELLED";
} else if ("SHIPPED".equals(state)) {
// 已发货状态的取消逻辑
System.out.println("订单已发货,无法取消");
} else if ("COMPLETED".equals(state)) {
// 已完成状态的取消逻辑
System.out.println("订单已完成,无法取消");
} else if ("CANCELLED".equals(state)) {
// 已取消状态的取消逻辑
System.out.println("订单已是取消状态");
}
}
// 每新增一个操作,就要重复一遍if-else地狱
}
这种写法的痛点:
- ❌ 条件判断爆炸:大量的if-else或switch-case语句
- ❌ 违反开闭原则:新增状态需要修改所有相关方法
- ❌ 代码臃肿:同一个方法包含所有状态的处理逻辑
- ❌ 难以维护:状态转换逻辑分散在各个方法中
- ❌ 容易出错:状态转换关系不清晰,容易遗漏
2. 状态模式:本质与定义
2.1 模式定义
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类。
2.2 模式结构
java
// 状态接口
public interface State {
void handle(Context context);
}
// 具体状态类
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("处理状态A的逻辑");
// 状态转换
context.setState(new ConcreteStateB());
}
}
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("处理状态B的逻辑");
// 状态转换
context.setState(new ConcreteStateA());
}
}
// 上下文类
public class Context {
private State currentState;
public Context(State initialState) {
this.currentState = initialState;
}
public void setState(State state) {
this.currentState = state;
System.out.println("状态已转换为: " + state.getClass().getSimpleName());
}
public void request() {
currentState.handle(this);
}
public State getCurrentState() {
return currentState;
}
}
3. 深入理解:状态模式的三重境界
3.1 第一重:行为与状态的绑定
核心思想:将每个状态的行为封装在独立的类中。
java
// 不好的做法:一个类包含所有状态的行为
public class BadOrder {
public void process() {
if (state == PENDING) { /* 逻辑A */ }
else if (state == PAID) { /* 逻辑B */ }
// ...
}
}
// 好的做法:每个状态一个类
public interface OrderState {
void process(Order order);
void cancel(Order order);
}
public class PendingState implements OrderState {
@Override
public void process(Order order) {
// 只关注待支付状态的处理逻辑
}
}
3.2 第二重:状态转换的集中管理
设计原则的体现:状态转换逻辑封装在状态类内部,上下文无需关心如何转换。
java
public class PaidState implements OrderState {
@Override
public void process(Order order) {
// 处理发货逻辑
order.ship();
// 状态转换:当前状态决定下一个状态
order.setState(new ShippedState());
}
}
3.3 第三重:状态机的显式建模
通过状态模式显式地建模状态机,使状态转换关系清晰可见。
java
// 状态机配置
public class StateMachine {
private Map<State, Map<Event, State>> transitions = new HashMap<>();
public void addTransition(State from, Event event, State to) {
transitions.computeIfAbsent(from, k -> new HashMap<>()).put(event, to);
}
public State getNextState(State current, Event event) {
return transitions.getOrDefault(current, Collections.emptyMap()).get(event);
}
}
4. 实战案例:完整的电商订单状态系统
让我们来看一个完整的例子:
java
// 订单状态接口
public interface OrderState {
void process(Order order);
void cancel(Order order);
void pay(Order order);
void ship(Order order);
void deliver(Order order);
void complete(Order order);
String getName();
}
// 具体状态类
@Slf4j
public class PendingPaymentState implements OrderState {
@Override
public void process(Order order) {
log.info("订单待支付,请先完成支付");
}
@Override
public void cancel(Order order) {
log.info("取消待支付订单");
order.setState(new CancelledState());
order.addLog("用户取消订单");
}
@Override
public void pay(Order order) {
log.info("处理订单支付");
// 支付处理逻辑
boolean paymentSuccess = processPayment(order);
if (paymentSuccess) {
order.setState(new PaidState());
order.addLog("支付成功");
} else {
log.warn("支付失败,订单保持待支付状态");
order.addLog("支付失败");
}
}
@Override
public void ship(Order order) {
log.warn("订单未支付,无法发货");
}
@Override
public void deliver(Order order) {
log.warn("订单未支付,无法收货");
}
@Override
public void complete(Order order) {
log.warn("订单未支付,无法完成");
}
@Override
public String getName() {
return "PENDING_PAYMENT";
}
private boolean processPayment(Order order) {
// 模拟支付处理
log.info("调用支付网关处理支付...");
return true; // 假设支付成功
}
}
@Slf4j
public class PaidState implements OrderState {
@Override
public void process(Order order) {
log.info("订单已支付,准备发货");
}
@Override
public void cancel(Order order) {
log.info("取消已支付订单,处理退款");
// 退款逻辑
processRefund(order);
order.setState(new CancelledState());
order.addLog("取消订单,退款处理中");
}
@Override
public void pay(Order order) {
log.warn("订单已支付,无需重复支付");
}
@Override
public void ship(Order order) {
log.info("订单发货处理");
// 发货逻辑
boolean shipSuccess = processShipping(order);
if (shipSuccess) {
order.setState(new ShippedState());
order.addLog("订单已发货");
}
}
@Override
public void deliver(Order order) {
log.warn("订单未发货,无法确认收货");
}
@Override
public void complete(Order order) {
log.warn("订单未完成配送,无法完成");
}
@Override
public String getName() {
return "PAID";
}
private boolean processShipping(Order order) {
log.info("生成发货单,通知仓库发货...");
return true;
}
private void processRefund(Order order) {
log.info("调用支付网关处理退款...");
}
}
@Slf4j
public class ShippedState implements OrderState {
@Override
public void process(Order order) {
log.info("订单已发货,等待确认收货");
}
@Override
public void cancel(Order order) {
log.warn("订单已发货,无法取消");
}
@Override
public void pay(Order order) {
log.warn("订单已支付,无需重复支付");
}
@Override
public void ship(Order order) {
log.warn("订单已发货,无需重复发货");
}
@Override
public void deliver(Order order) {
log.info("确认订单收货");
order.setState(new DeliveredState());
order.addLog("用户确认收货");
}
@Override
public void complete(Order order) {
log.warn("订单未收货,无法完成");
}
@Override
public String getName() {
return "SHIPPED";
}
}
@Slf4j
public class DeliveredState implements OrderState {
@Override
public void process(Order order) {
log.info("订单已送达,等待用户确认完成");
}
@Override
public void cancel(Order order) {
log.warn("订单已送达,无法取消");
}
@Override
public void pay(Order order) {
log.warn("订单已完成支付,无需重复支付");
}
@Override
public void ship(Order order) {
log.warn("订单已发货,无需重复发货");
}
@Override
public void deliver(Order order) {
log.warn("订单已收货,无需重复确认");
}
@Override
public void complete(Order order) {
log.info("完成订单");
order.setState(new CompletedState());
order.addLog("订单完成");
}
@Override
public String getName() {
return "DELIVERED";
}
}
@Slf4j
public class CompletedState implements OrderState {
@Override
public void process(Order order) {
log.info("订单已完成,感谢购买");
}
@Override
public void cancel(Order order) {
log.warn("订单已完成,无法取消");
}
@Override
public void pay(Order order) {
log.warn("订单已完成支付,无需重复支付");
}
@Override
public void ship(Order order) {
log.warn("订单已完成发货,无需重复发货");
}
@Override
public void deliver(Order order) {
log.warn("订单已完成收货,无需重复确认");
}
@Override
public void complete(Order order) {
log.warn("订单已完成,无需重复完成");
}
@Override
public String getName() {
return "COMPLETED";
}
}
@Slf4j
public class CancelledState implements OrderState {
@Override
public void process(Order order) {
log.warn("订单已取消,无法处理");
}
@Override
public void cancel(Order order) {
log.warn("订单已是取消状态");
}
@Override
public void pay(Order order) {
log.warn("订单已取消,无法支付");
}
@Override
public void ship(Order order) {
log.warn("订单已取消,无法发货");
}
@Override
public void deliver(Order order) {
log.warn("订单已取消,无法收货");
}
@Override
public void complete(Order order) {
log.warn("订单已取消,无法完成");
}
@Override
public String getName() {
return "CANCELLED";
}
}
// 订单类 - 上下文
@Slf4j
public class Order {
private String orderId;
private OrderState state;
private List<String> logs = new ArrayList<>();
private LocalDateTime createTime;
private BigDecimal amount;
public Order(String orderId, BigDecimal amount) {
this.orderId = orderId;
this.amount = amount;
this.state = new PendingPaymentState();
this.createTime = LocalDateTime.now();
this.logs.add("订单创建");
log.info("创建新订单: {},金额: {},初始状态: {}", orderId, amount, state.getName());
}
// 委托给当前状态对象
public void process() {
log.info("处理订单: {}", orderId);
state.process(this);
}
public void cancel() {
log.info("取消订单: {}", orderId);
state.cancel(this);
}
public void pay() {
log.info("支付订单: {}", orderId);
state.pay(this);
}
public void ship() {
log.info("发货订单: {}", orderId);
state.ship(this);
}
public void deliver() {
log.info("确认收货订单: {}", orderId);
state.deliver(this);
}
public void complete() {
log.info("完成订单: {}", orderId);
state.complete(this);
}
// 状态管理
public void setState(OrderState state) {
this.state = state;
log.debug("订单 {} 状态变更为: {}", orderId, state.getName());
}
public String getCurrentState() {
return state.getName();
}
public void addLog(String message) {
String logEntry = String.format("[%s] %s",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
message);
logs.add(logEntry);
}
public void printStatus() {
System.out.println("=== 订单状态 ===");
System.out.println("订单号: " + orderId);
System.out.println("当前状态: " + getCurrentState());
System.out.println("创建时间: " + createTime);
System.out.println("订单金额: " + amount);
System.out.println("操作日志:");
logs.forEach(log -> System.out.println(" " + log));
System.out.println("==============");
}
// getters
public String getOrderId() { return orderId; }
public BigDecimal getAmount() { return amount; }
public List<String> getLogs() { return new ArrayList<>(logs); }
}
// 使用示例
@Slf4j
public class OrderStateDemo {
public static void main(String[] args) {
// 创建订单
Order order = new Order("ORDER_001", new BigDecimal("199.99"));
order.printStatus();
// 正常流程
order.pay(); // 支付
order.ship(); // 发货
order.deliver(); // 收货
order.complete(); // 完成
order.printStatus();
// 创建另一个订单测试取消流程
Order order2 = new Order("ORDER_002", new BigDecimal("99.50"));
order2.cancel(); // 取消
order2.printStatus();
// 测试非法操作
order2.pay(); // 已取消的订单尝试支付
}
}
5. Spring Boot中的优雅实现
在Spring Boot中,我们可以利用状态机框架和依赖注入让状态模式更加优雅:
java
// 状态枚举
public enum OrderStatus {
PENDING_PAYMENT,
PAID,
SHIPPED,
DELIVERED,
COMPLETED,
CANCELLED
}
// 事件枚举
public enum OrderEvent {
PAY,
CANCEL,
SHIP,
DELIVER,
COMPLETE
}
// 状态机配置
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states)
throws Exception {
states
.withStates()
.initial(OrderStatus.PENDING_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class))
.end(OrderStatus.COMPLETED)
.end(OrderStatus.CANCELLED);
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.PENDING_PAYMENT).target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderStatus.PENDING_PAYMENT).target(OrderStatus.CANCELLED)
.event(OrderEvent.CANCEL)
.and()
.withExternal()
.source(OrderStatus.PAID).target(OrderStatus.SHIPPED)
.event(OrderEvent.SHIP)
.and()
.withExternal()
.source(OrderStatus.PAID).target(OrderStatus.CANCELLED)
.event(OrderEvent.CANCEL)
.and()
.withExternal()
.source(OrderStatus.SHIPPED).target(OrderStatus.DELIVERED)
.event(OrderEvent.DELIVER)
.and()
.withExternal()
.source(OrderStatus.DELIVERED).target(OrderStatus.COMPLETED)
.event(OrderEvent.COMPLETE);
}
}
// 订单实体
@Entity
@Table(name = "orders")
@Data
public class Order {
@Id
private String id;
private BigDecimal amount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@CreationTimestamp
private LocalDateTime createTime;
@UpdateTimestamp
private LocalDateTime updateTime;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderLog> logs = new ArrayList<>();
// 状态机不持久化
@Transient
private StateMachine<OrderStatus, OrderEvent> stateMachine;
}
// 订单日志
@Entity
@Table(name = "order_logs")
@Data
public class OrderLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
private String message;
@CreationTimestamp
private LocalDateTime createTime;
}
// 订单服务
@Service
@Transactional
@Slf4j
public class OrderService {
private final OrderRepository orderRepository;
private final StateMachineFactory<OrderStatus, OrderEvent> stateMachineFactory;
private final OrderLogRepository logRepository;
public OrderService(OrderRepository orderRepository,
StateMachineFactory<OrderStatus, OrderEvent> stateMachineFactory,
OrderLogRepository logRepository) {
this.orderRepository = orderRepository;
this.stateMachineFactory = stateMachineFactory;
this.logRepository = logRepository;
}
public Order createOrder(BigDecimal amount) {
Order order = new Order();
order.setId("ORDER_" + System.currentTimeMillis());
order.setAmount(amount);
order.setStatus(OrderStatus.PENDING_PAYMENT);
order = orderRepository.save(order);
addLog(order, "订单创建");
log.info("创建订单: {},金额: {}", order.getId(), amount);
return order;
}
public Order processEvent(String orderId, OrderEvent event) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("订单不存在: " + orderId));
StateMachine<OrderStatus, OrderEvent> stateMachine = buildStateMachine(order);
// 发送事件
boolean accepted = stateMachine.sendEvent(event);
if (accepted) {
// 更新订单状态
OrderStatus newStatus = stateMachine.getState().getId();
order.setStatus(newStatus);
order = orderRepository.save(order);
addLog(order, "执行操作: " + event + ",状态变更为: " + newStatus);
log.info("订单 {} 执行操作 {},新状态: {}", orderId, event, newStatus);
} else {
log.warn("订单 {} 无法执行操作 {},当前状态: {}", orderId, event, order.getStatus());
throw new IllegalStateException("无法执行操作: " + event + ",当前状态: " + order.getStatus());
}
return order;
}
public boolean canProcessEvent(String orderId, OrderEvent event) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("订单不存在: " + orderId));
StateMachine<OrderStatus, OrderEvent> stateMachine = buildStateMachine(order);
return stateMachine.sendEvent(event);
}
public List<OrderEvent> getAvailableEvents(String orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("订单不存在: " + orderId));
StateMachine<OrderStatus, OrderEvent> stateMachine = buildStateMachine(order);
return stateMachine.getTransitions().stream()
.filter(transition -> transition.getSource().getId() == order.getStatus())
.map(transition -> transition.getTrigger().getEvent())
.collect(Collectors.toList());
}
private StateMachine<OrderStatus, OrderEvent> buildStateMachine(Order order) {
StateMachine<OrderStatus, OrderEvent> stateMachine = stateMachineFactory.getStateMachine();
stateMachine.start();
stateMachine.getStateMachineAccessor().doWithAllRegions(access -> {
access.resetStateMachine(new DefaultStateMachineContext<>(order.getStatus(), null, null, null));
});
return stateMachine;
}
private void addLog(Order order, String message) {
OrderLog log = new OrderLog();
log.setOrder(order);
log.setMessage(message);
logRepository.save(log);
}
}
// REST控制器
@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
Order order = orderService.createOrder(request.getAmount());
return ResponseEntity.ok(order);
}
@PostMapping("/{orderId}/events/{event}")
public ResponseEntity<Order> processEvent(@PathVariable String orderId,
@PathVariable OrderEvent event) {
try {
Order order = orderService.processEvent(orderId, event);
return ResponseEntity.ok(order);
} catch (IllegalStateException e) {
return ResponseEntity.badRequest().build();
}
}
@GetMapping("/{orderId}/available-events")
public ResponseEntity<List<OrderEvent>> getAvailableEvents(@PathVariable String orderId) {
List<OrderEvent> events = orderService.getAvailableEvents(orderId);
return ResponseEntity.ok(events);
}
@GetMapping("/{orderId}/can-process/{event}")
public ResponseEntity<Boolean> canProcessEvent(@PathVariable String orderId,
@PathVariable OrderEvent event) {
boolean canProcess = orderService.canProcessEvent(orderId, event);
return ResponseEntity.ok(canProcess);
}
}
6. 状态模式的变体与进阶用法
6.1 状态模式 + 策略模式
结合策略模式实现更灵活的状态行为:
java
// 状态行为策略
public interface StateBehavior {
void onEnter();
void onExit();
boolean canTransitionTo(State nextState);
}
// 带行为的订单状态
public abstract class BehaviorOrderState implements OrderState {
protected StateBehavior behavior;
public BehaviorOrderState(StateBehavior behavior) {
this.behavior = behavior;
this.behavior.onEnter();
}
@Override
public void process(Order order) {
// 默认实现
}
public void transitionTo(Order order, OrderState nextState) {
if (behavior.canTransitionTo((State) nextState)) {
behavior.onExit();
order.setState(nextState);
} else {
throw new IllegalStateException("无法转换到目标状态");
}
}
}
6.2 有限状态机(FSM)实现
java
// 状态机
@Slf4j
public class StateMachine<S, E> {
private S currentState;
private final Map<S, Map<E, S>> transitions = new HashMap<>();
private final Map<S, Runnable> onEnterCallbacks = new HashMap<>();
private final Map<S, Runnable> onExitCallbacks = new HashMap<>();
public StateMachine(S initialState) {
this.currentState = initialState;
onEnterCallbacks.getOrDefault(initialState, () -> {}).run();
}
public void addTransition(S from, E event, S to) {
transitions.computeIfAbsent(from, k -> new HashMap<>()).put(event, to);
}
public void onEnter(S state, Runnable callback) {
onEnterCallbacks.put(state, callback);
}
public void onExit(S state, Runnable callback) {
onExitCallbacks.put(state, callback);
}
public boolean sendEvent(E event) {
Map<E, S> stateTransitions = transitions.get(currentState);
if (stateTransitions != null && stateTransitions.containsKey(event)) {
S nextState = stateTransitions.get(event);
// 执行退出回调
onExitCallbacks.getOrDefault(currentState, () -> {}).run();
// 转换状态
currentState = nextState;
log.info("状态转换: {} -> {}", currentState, nextState);
// 执行进入回调
onEnterCallbacks.getOrDefault(nextState, () -> {}).run();
return true;
}
return false;
}
public S getCurrentState() {
return currentState;
}
public List<E> getAvailableEvents() {
Map<E, S> stateTransitions = transitions.get(currentState);
return stateTransitions != null ? new ArrayList<>(stateTransitions.keySet()) : Collections.emptyList();
}
}
7. 状态模式 vs 其他模式
7.1 状态模式 vs 策略模式
- 策略模式:客户端主动选择策略,策略之间相互独立
- 状态模式:状态自动转换,状态之间有关联关系
7.2 状态模式 vs 状态机模式
- 状态模式:设计模式,关注对象行为随状态变化
- 状态机模式:架构模式,关注状态转换的建模和管理
7.3 状态模式 vs 条件判断
- 条件判断:简单的if-else,适合状态少、逻辑简单
- 状态模式:状态多、转换复杂,需要更好的扩展性
8. 总结与思考
8.1 状态模式的优点
- 单一职责原则:每个状态一个类,职责清晰
- 开闭原则:新增状态无需修改现有代码
- 消除条件判断:用多态代替复杂的条件判断
- 状态转换明确:状态转换逻辑集中管理
- 易于测试:每个状态可以独立测试
8.2 状态模式的缺点
- 类数量增加:每个状态一个类,可能产生很多小类
- 复杂度增加:简单的状态机可能过度设计
- 状态共享困难:状态之间共享数据比较麻烦
- 学习成本:理解双分派和状态转换需要时间
8.3 深入思考
状态模式的本质是**"行为的状态化"**。它将对象的行为按照状态进行划分,让每个状态专注于自己的职责,通过状态转换来实现完整的行为流程。
设计之美的思考:
"状态模式教会我们用'分治'的思想来管理复杂的状态逻辑。就像现实世界中的工作流程一样,每个阶段都有明确的职责和边界,阶段之间的转换也有清晰的规则。这种结构化的思维方式让我们的代码更加清晰和健壮。"
从源码的角度看,状态模式在Java中有着广泛应用:
- Java线程的状态管理
- Servlet的生命周期管理
- Spring状态机框架
- 工作流引擎的实现
- 游戏开发中的角色状态管理
何时使用状态模式:
- 对象的行为取决于它的状态,并且必须在运行时根据状态改变行为
- 操作中有大量的条件语句,这些条件依赖于对象的状态
- 状态转换逻辑复杂,需要清晰的管理
- 需要容易地添加新的状态和转换
使用场景:
- 订单和工作流系统
- 游戏角色和NPC行为
- UI组件的状态管理
- 网络协议的状态处理
- 硬件设备的控制逻辑
下一篇预告:设计模式手册020 - 模板方法 - 定义算法骨架的艺术
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。