Appearance
状态模式 (State)
一、定义
一句话概括:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
官方定义:Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
二、解决的问题
2.1 问题场景
很多对象的行为取决于其当前状态。例如:
- 订单:待支付、已支付、已发货、已完成、已取消 — 不同状态下操作不同
- 电梯:静止、上升、下降、开门、关门
- TCP 连接:CLOSED、LISTEN、SYN_SENT、ESTABLISHED 等
- 游戏角色:站立、行走、跑步、跳跃、攻击
2.2 不用状态模式会怎样?
java
// 反例:大量 if-else 判断状态
class Order {
private String status;
public void pay() {
if ("CREATED".equals(status)) {
status = "PAID";
System.out.println("支付成功");
} else if ("PAID".equals(status)) {
System.out.println("已支付,无需重复支付");
} else if ("CANCELLED".equals(status)) {
System.out.println("订单已取消,无法支付");
}
// 每个方法都需要大量 if-else
}
public void ship() {
if ("PAID".equals(status)) {
status = "SHIPPED";
} else {
System.out.println("当前状态无法发货");
}
}
// 新增状态需要修改所有方法,违反开闭原则
}问题:
- 大量 if-else 或 switch-case,代码难以维护
- 新增状态需要修改所有相关方法,违反开闭原则
- 状态转换逻辑分散在各处,难以追踪
三、结构
3.1 角色组成
| 角色 | 说明 |
|---|---|
| Context(上下文) | 持有当前状态,定义客户端感兴趣的接口 |
| State(抽象状态) | 定义与 Context 状态相关的行为接口 |
| ConcreteState(具体状态) | 实现 State 接口,定义该状态下的具体行为 |
3.2 类图(ASCII)
┌──────────────┐ ┌──────────────┐
│ Context │────────►│ State │
├──────────────┤ ├──────────────┤
│ - state │ │ + handle() │
│ + request() │ └──────┬───────┘
│ + setState() │ │
└──────────────┘ ┌──────┴──────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│Concrete │ │Concrete │
│StateA │ │StateB │
└──────────┘ └──────────┘四、代码实现
4.1 基础实现
java
// ==================== 抽象状态 ====================
interface OrderState {
void pay(OrderContext context);
void ship(OrderContext context);
void cancel(OrderContext context);
String getStatusName();
}
// ==================== 具体状态:待支付 ====================
class CreatedState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("支付成功,订单状态变更为:已支付");
context.setState(new PaidState());
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:待支付订单无法发货");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消");
context.setState(new CancelledState());
}
@Override
public String getStatusName() { return "待支付"; }
}
// ==================== 具体状态:已支付 ====================
class PaidState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("订单已支付,无需重复支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("发货成功,订单状态变更为:已发货");
context.setState(new ShippedState());
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消,将发起退款");
context.setState(new CancelledState());
}
@Override
public String getStatusName() { return "已支付"; }
}
// ==================== 具体状态:已发货 ====================
class ShippedState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:已发货订单无法支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:已发货订单无法重复发货");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消,将发起退货退款");
context.setState(new CancelledState());
}
@Override
public String getStatusName() { return "已发货"; }
}
// ==================== 具体状态:已取消 ====================
class CancelledState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:已取消订单无法支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:已取消订单无法发货");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消,无需重复操作");
}
@Override
public String getStatusName() { return "已取消"; }
}
// ==================== 上下文 ====================
class OrderContext {
private OrderState state;
private Long orderId;
public OrderContext(Long orderId) {
this.orderId = orderId;
this.state = new CreatedState(); // 初始状态
}
public void setState(OrderState state) {
this.state = state;
}
public void pay() { state.pay(this); }
public void ship() { state.ship(this); }
public void cancel() { state.cancel(this); }
public String getCurrentStatus() {
return state.getStatusName();
}
}
// ==================== 客户端 ====================
public class StateDemo {
public static void main(String[] args) {
OrderContext order = new OrderContext(1001L);
System.out.println("当前状态:" + order.getCurrentStatus());
order.pay(); // 支付成功 → 已支付
order.ship(); // 发货成功 → 已发货
order.pay(); // 错误:已发货订单无法支付
System.out.println("当前状态:" + order.getCurrentStatus());
}
}4.2 进阶实现
4.2.1 状态模式 vs 策略模式
| 维度 | 状态模式 | 策略模式 |
|---|---|---|
| 关注点 | 对象状态变化导致行为变化 | 算法/策略的可替换性 |
| 状态切换 | 由 Context 自身或 State 触发 | 由客户端主动选择 |
| 生命周期 | State 之间相互感知,可互相切换 | 策略之间互不感知,独立存在 |
| 客户端感知 | 客户端通常不直接选择状态 | 客户端主动选择策略 |
| 典型场景 | 订单状态机、开关状态 | 支付方式、排序算法 |
java
// 状态模式:State 之间可以互相切换
class TVOnState implements TVState {
public void pressPower(TVContext tv) {
tv.setState(new TVOffState()); // 状态自己决定下一个状态
}
}
// 策略模式:策略之间互不关心
class AlipayStrategy implements PaymentStrategy {
public void pay() { /* 支付逻辑 */ }
// 不会主动切换到 WechatPayStrategy
}4.2.2 Spring StateMachine
java
// Spring StateMachine 配置示例
@Configuration
@EnableStateMachine
class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states)
throws Exception {
states.withStates()
.initial("CREATED")
.state("PAID")
.state("SHIPPED")
.state("COMPLETED")
.end("CANCELLED");
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions)
throws Exception {
transitions
.withExternal()
.source("CREATED").target("PAID").event("PAY")
.and()
.withExternal()
.source("PAID").target("SHIPPED").event("SHIP")
.and()
.withExternal()
.source("SHIPPED").target("COMPLETED").event("COMPLETE")
.and()
.withExternal()
.source("CREATED").target("CANCELLED").event("CANCEL")
.and()
.withExternal()
.source("PAID").target("CANCELLED").event("CANCEL");
}
}4.3 生产级实现
Spring Boot 订单状态机
java
// ==================== 枚举状态 ====================
enum OrderStatus {
CREATED, PAID, SHIPPED, COMPLETED, CANCELLED, REFUNDING, REFUNDED
}
// ==================== 抽象状态处理器 ====================
interface OrderStateHandler {
OrderStatus getStatus();
void handle(OrderEntity order);
Set<OrderStatus> getAllowedTransitions();
}
// ==================== 具体状态处理器 ====================
@Component
class CreatedStateHandler implements OrderStateHandler {
@Override
public OrderStatus getStatus() { return OrderStatus.CREATED; }
@Override
public void handle(OrderEntity order) {
// 状态进入时的处理逻辑
System.out.println("订单创建: " + order.getId());
}
@Override
public Set<OrderStatus> getAllowedTransitions() {
return Set.of(OrderStatus.PAID, OrderStatus.CANCELLED);
}
}
@Component
class PaidStateHandler implements OrderStateHandler {
@Override
public OrderStatus getStatus() { return OrderStatus.PAID; }
@Override
public void handle(OrderEntity order) {
System.out.println("订单支付成功: " + order.getId());
}
@Override
public Set<OrderStatus> getAllowedTransitions() {
return Set.of(OrderStatus.SHIPPED, OrderStatus.CANCELLED, OrderStatus.REFUNDING);
}
}
// ==================== 状态机服务 ====================
@Service
class OrderStateMachineService {
private final Map<OrderStatus, OrderStateHandler> handlers = new HashMap<>();
@Autowired
public OrderStateMachineService(List<OrderStateHandler> handlerList) {
for (OrderStateHandler handler : handlerList) {
handlers.put(handler.getStatus(), handler);
}
}
public void transition(OrderEntity order, OrderStatus targetStatus) {
OrderStatus currentStatus = order.getStatus();
OrderStateHandler handler = handlers.get(currentStatus);
if (handler == null) {
throw new IllegalStateException("未知状态: " + currentStatus);
}
if (!handler.getAllowedTransitions().contains(targetStatus)) {
throw new IllegalStateException(
"不允许从 " + currentStatus + " 转换到 " + targetStatus);
}
order.setStatus(targetStatus);
handlers.get(targetStatus).handle(order);
}
}
// ==================== Controller ====================
@RestController
@RequestMapping("/api/orders")
class OrderStateController {
@Autowired private OrderStateMachineService stateMachine;
@Autowired private OrderRepository orderRepository;
@PostMapping("/{id}/pay")
public OrderEntity pay(@PathVariable Long id) {
OrderEntity order = orderRepository.findById(id)
.orElseThrow(() -> new RuntimeException("订单不存在"));
stateMachine.transition(order, OrderStatus.PAID);
return orderRepository.save(order);
}
@PostMapping("/{id}/ship")
public OrderEntity ship(@PathVariable Long id) {
OrderEntity order = orderRepository.findById(id)
.orElseThrow(() -> new RuntimeException("订单不存在"));
stateMachine.transition(order, OrderStatus.SHIPPED);
return orderRepository.save(order);
}
}五、优缺点
优点
- 消除 if-else:将状态判断逻辑分散到各个状态类中,代码更清晰
- 符合开闭原则:新增状态只需新增一个状态类,无需修改现有代码
- 状态转换显式化:状态转换逻辑集中在状态类中,易于追踪
- 避免状态不一致:状态类可以控制合法的状态转换
缺点
- 类数量增加:每个状态都需要一个类
- 状态类相互依赖:状态类之间需要知道对方的存在
- Context 职责增加:Context 需要维护状态转换逻辑
六、适用场景
- 订单系统:订单状态的流转(创建 → 支付 → 发货 → 完成)
- 工作流引擎:审批流程的状态流转
- 游戏开发:角色状态(站立、行走、跑步、攻击、死亡)
- 网络协议:TCP 连接状态机
- 电梯控制:电梯运行状态
- 自动售货机:投币、选择、出货、找零
七、JDK / Spring 框架中的实际应用
| 框架 | 应用位置 | 说明 |
|---|---|---|
| Spring | Spring StateMachine | 状态机框架 |
| Spring | Lifecycle 接口 | 组件的生命周期状态 |
| JSF | Lifecycle | JSF 请求处理生命周期 |
| Netty | ChannelState | Channel 状态管理 |
| JDK | Thread.State | 线程状态(NEW, RUNNABLE, WAITING 等) |
八、与其他模式的关系
与策略模式(重点区别)
- 结构相似:类图几乎一样,都是 Context 持有接口引用
- 意图不同:状态模式关注"状态切换导致行为变化",策略模式关注"算法的可替换性"
- 最核心区别:状态模式中,State 之间可以互相切换(知道彼此存在);策略模式中,策略之间互不感知
与享元模式
- 状态对象通常是单例的,可以使用享元模式共享,减少对象创建
与责任链模式
- 可以使用责任链模式处理状态之间的转换逻辑
九、面试常见问题
Q1:状态模式和策略模式有什么区别?
A:两者结构相似,但意图不同。状态模式中,状态对象知道彼此的存在,可以自行切换状态(如 CreatedState 在 pay() 后切换到 PaidState);策略模式中,策略对象互不感知,由客户端选择策略。状态模式关注"状态变化导致行为变化",策略模式关注"算法可替换"。
Q2:状态模式如何处理状态转换的合法性校验?
A:可以在每个状态类中维护一个允许转换的目标状态集合(getAllowedTransitions()),在 Context 或状态机中调用 transition() 前进行校验,不合法则抛出异常。也可以使用 Spring StateMachine 等框架,通过配置声明式地定义合法状态转换。
Q3:状态模式中状态对象应该使用单例还是每次创建?
A:如果状态对象是无状态的(不持有实例变量),应该使用单例,通过享元模式共享,减少内存开销。如果状态对象持有状态相关的数据,则需要每次创建新实例。大多数情况下,状态对象是无状态的,建议使用单例。
Q4:如果状态非常多(如 50+ 个状态),状态模式还适用吗?
A:如果状态非常多,可以考虑使用状态表(State Table)替代状态模式,用一个二维数组或 Map 存储状态转换关系。或者使用 Spring StateMachine 等状态机框架,通过声明式配置管理状态转换。状态模式更适合状态数量适中(5-15 个)的场景。
