状态模式(State Pattern)
一、模式概述
状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
核心思想:将每个状态封装成独立的类,状态转换的逻辑分布在各个状态类中,而不是集中在 if-else 或 switch 语句中。当对象的状态改变时,其行为也会相应改变。
二、角色说明
| 角色 | 说明 |
|---|---|
| Context(上下文) | 维护当前状态对象的引用,将与状态相关的行为委托给当前状态对象 |
| State(抽象状态) | 定义与状态相关的行为接口 |
| ConcreteState(具体状态) | 实现与状态相关的行为,决定状态如何转换 |
三、生活中的例子
- 交通信号灯:红灯 -> 绿灯 -> 黄灯 -> 红灯,每个状态的行为不同
- 订单状态:待支付 -> 已支付 -> 已发货 -> 已完成 -> 已取消
- 游戏角色状态:站立、行走、奔跑、跳跃、攻击、死亡
- TCP 连接状态:LISTEN、SYN_SENT、ESTABLISHED、CLOSE_WAIT、TIME_WAIT
- 自动售货机:空闲、投币、选择商品、出货、找零
四、示例1:订单状态机
问题场景
电商订单有多种状态,每种状态下可以执行的操作不同。
java
// ========== 抽象状态 ==========
/**
* 订单状态接口
* 定义了订单在各个状态下可以执行的操作
*/
interface OrderState {
void pay(Order order);
void ship(Order order);
void deliver(Order order);
void cancel(Order order);
void refund(Order order);
String getStateName();
}
// ========== 具体状态 ==========
/**
* 待支付状态
*/
class PendingPaymentState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [待支付] 订单支付成功!金额: " + order.getAmount());
order.setState(new PaidState());
}
@Override
public void ship(Order order) {
System.out.println(" [待支付] 错误:未支付不能发货!");
}
@Override
public void deliver(Order order) {
System.out.println(" [待支付] 错误:未支付不能确认收货!");
}
@Override
public void cancel(Order order) {
System.out.println(" [待支付] 订单已取消");
order.setState(new CancelledState());
}
@Override
public void refund(Order order) {
System.out.println(" [待支付] 错误:未支付不能退款!");
}
@Override
public String getStateName() { return "待支付"; }
}
/**
* 已支付状态
*/
class PaidState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [已支付] 错误:订单已支付!");
}
@Override
public void ship(Order order) {
System.out.println(" [已支付] 订单已发货!物流单号: SF" + System.currentTimeMillis());
order.setState(new ShippedState());
}
@Override
public void deliver(Order order) {
System.out.println(" [已支付] 错误:未发货不能确认收货!");
}
@Override
public void cancel(Order order) {
System.out.println(" [已支付] 订单取消,退款 " + order.getAmount() + " 元");
order.setState(new CancelledState());
}
@Override
public void refund(Order order) {
System.out.println(" [已支付] 订单退款 " + order.getAmount() + " 元");
order.setState(new RefundedState());
}
@Override
public String getStateName() { return "已支付"; }
}
/**
* 已发货状态
*/
class ShippedState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [已发货] 错误:订单已支付!");
}
@Override
public void ship(Order order) {
System.out.println(" [已发货] 错误:订单已发货!");
}
@Override
public void deliver(Order order) {
System.out.println(" [已发货] 确认收货成功!");
order.setState(new DeliveredState());
}
@Override
public void cancel(Order order) {
System.out.println(" [已发货] 错误:已发货不能取消,请申请退货!");
}
@Override
public void refund(Order order) {
System.out.println(" [已发货] 错误:已发货不能直接退款,请申请退货!");
}
@Override
public String getStateName() { return "已发货"; }
}
/**
* 已签收状态
*/
class DeliveredState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [已签收] 错误:订单已完成!");
}
@Override
public void ship(Order order) {
System.out.println(" [已签收] 错误:订单已完成!");
}
@Override
public void deliver(Order order) {
System.out.println(" [已签收] 错误:已经确认收货!");
}
@Override
public void cancel(Order order) {
System.out.println(" [已签收] 错误:已签收不能取消,请申请售后!");
}
@Override
public void refund(Order order) {
System.out.println(" [已签收] 申请退货退款,退款 " + order.getAmount() + " 元");
order.setState(new RefundedState());
}
@Override
public String getStateName() { return "已签收"; }
}
/**
* 已取消状态
*/
class CancelledState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [已取消] 错误:订单已取消!");
}
@Override
public void ship(Order order) {
System.out.println(" [已取消] 错误:订单已取消!");
}
@Override
public void deliver(Order order) {
System.out.println(" [已取消] 错误:订单已取消!");
}
@Override
public void cancel(Order order) {
System.out.println(" [已取消] 错误:订单已取消!");
}
@Override
public void refund(Order order) {
System.out.println(" [已取消] 错误:订单已取消!");
}
@Override
public String getStateName() { return "已取消"; }
}
/**
* 已退款状态
*/
class RefundedState implements OrderState {
@Override
public void pay(Order order) {
System.out.println(" [已退款] 错误:订单已退款!");
}
@Override
public void ship(Order order) {
System.out.println(" [已退款] 错误:订单已退款!");
}
@Override
public void deliver(Order order) {
System.out.println(" [已退款] 错误:订单已退款!");
}
@Override
public void cancel(Order order) {
System.out.println(" [已退款] 错误:订单已退款!");
}
@Override
public void refund(Order order) {
System.out.println(" [已退款] 错误:订单已退款!");
}
@Override
public String getStateName() { return "已退款"; }
}
// ========== 上下文 ==========
/**
* 订单(上下文)
* 将与状态相关的行为委托给当前状态对象
*/
class Order {
private String orderId;
private double amount;
private OrderState currentState;
public Order(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
this.currentState = new PendingPaymentState(); // 初始状态:待支付
}
public void setState(OrderState state) {
System.out.println(" [状态转换] " + currentState.getStateName()
+ " -> " + state.getStateName());
this.currentState = state;
}
public void pay() { currentState.pay(this); }
public void ship() { currentState.ship(this); }
public void deliver() { currentState.deliver(this); }
public void cancel() { currentState.cancel(this); }
public void refund() { currentState.refund(this); }
public String getOrderId() { return orderId; }
public double getAmount() { return amount; }
public String getCurrentStateName() { return currentState.getStateName(); }
public void showStatus() {
System.out.println(" [订单状态] " + orderId + " - " + currentState.getStateName()
+ " - 金额: " + amount);
}
}
// ========== 客户端代码 ==========
public class OrderStateDemo {
public static void main(String[] args) {
System.out.println("===== 创建订单 =====");
Order order = new Order("ORD20250101", 299.0);
order.showStatus();
System.out.println("\n===== 正常流程:支付 -> 发货 -> 收货 =====");
order.pay();
order.ship();
order.deliver();
System.out.println("\n===== 尝试在已完成的订单上操作 =====");
order.cancel();
order.refund();
System.out.println("\n\n===== 新订单:支付后取消 =====");
Order order2 = new Order("ORD20250102", 199.0);
order2.pay();
order2.cancel();
System.out.println("\n===== 新订单:支付后退款 =====");
Order order3 = new Order("ORD20250103", 399.0);
order3.pay();
order3.refund();
System.out.println("\n===== 新订单:直接取消(未支付) =====");
Order order4 = new Order("ORD20250104", 99.0);
order4.cancel();
}
}运行结果
===== 创建订单 =====
[订单状态] ORD20250101 - 待支付 - 金额: 299.0
===== 正常流程:支付 -> 发货 -> 收货 =====
[待支付] 订单支付成功!金额: 299.0
[状态转换] 待支付 -> 已支付
[已支付] 订单已发货!物流单号: SF1735689600000
[状态转换] 已支付 -> 已发货
[已发货] 确认收货成功!
[状态转换] 已发货 -> 已签收
===== 尝试在已完成的订单上操作 =====
[已签收] 错误:已签收不能取消,请申请售后!
[已签收] 申请退货退款,退款 299.0 元
[状态转换] 已签收 -> 已退款五、示例2:TCP 连接状态
问题场景
模拟 TCP 连接的状态转换。
java
import java.util.HashMap;
import java.util.Map;
// ========== 抽象状态 ==========
/**
* TCP 连接状态接口
*/
interface TCPState {
void connect(TCPConnection connection);
void disconnect(TCPConnection connection);
void acknowledge(TCPConnection connection);
void sendData(TCPConnection connection, String data);
String getStateName();
}
// ========== 具体状态 ==========
/**
* CLOSED 状态:初始状态
*/
class ClosedState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [CLOSED] 发送 SYN 包,连接中...");
connection.setState(new ListenState());
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [CLOSED] 已经是关闭状态");
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [CLOSED] 无法确认,未建立连接");
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [CLOSED] 无法发送数据,未建立连接");
}
@Override
public String getStateName() { return "CLOSED"; }
}
/**
* LISTEN 状态:等待连接
*/
class ListenState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [LISTEN] 收到 SYN,发送 SYN+ACK...");
connection.setState(new SynReceivedState());
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [LISTEN] 取消监听,关闭连接");
connection.setState(new ClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [LISTEN] 收到 ACK,建立连接!");
connection.setState(new EstablishedState());
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [LISTEN] 无法发送数据,连接未建立");
}
@Override
public String getStateName() { return "LISTEN"; }
}
/**
* SYN_SENT 状态:已发送 SYN,等待回复
*/
class SynSentState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [SYN_SENT] 已经发送 SYN,等待回复...");
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [SYN_SENT] 连接超时,关闭连接");
connection.setState(new ClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [SYN_SENT] 收到 SYN+ACK,发送 ACK...");
connection.setState(new EstablishedState());
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [SYN_SENT] 无法发送数据,连接未建立");
}
@Override
public String getStateName() { return "SYN_SENT"; }
}
/**
* SYN_RECEIVED 状态:已收到 SYN,等待 ACK
*/
class SynReceivedState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [SYN_RECEIVED] 已收到 SYN,等待 ACK...");
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [SYN_RECEIVED] 连接失败,关闭连接");
connection.setState(new ClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [SYN_RECEIVED] 收到 ACK,连接建立!");
connection.setState(new EstablishedState());
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [SYN_RECEIVED] 无法发送数据,连接未建立");
}
@Override
public String getStateName() { return "SYN_RECEIVED"; }
}
/**
* ESTABLISHED 状态:连接已建立
*/
class EstablishedState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [ESTABLISHED] 连接已经建立");
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [ESTABLISHED] 发送 FIN,准备关闭...");
connection.setState(new FinWaitState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [ESTABLISHED] 收到确认");
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [ESTABLISHED] 发送数据: " + data);
}
@Override
public String getStateName() { return "ESTABLISHED"; }
}
/**
* FIN_WAIT 状态:等待关闭
*/
class FinWaitState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [FIN_WAIT] 正在关闭连接...");
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [FIN_WAIT] 连接已关闭");
connection.setState(new ClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [FIN_WAIT] 收到 FIN+ACK,发送 ACK...");
connection.setState(new TimeWaitState());
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [FIN_WAIT] 无法发送数据,连接正在关闭");
}
@Override
public String getStateName() { return "FIN_WAIT"; }
}
/**
* TIME_WAIT 状态:等待确认关闭
*/
class TimeWaitState implements TCPState {
@Override
public void connect(TCPConnection connection) {
System.out.println(" [TIME_WAIT] 等待关闭确认...");
}
@Override
public void disconnect(TCPConnection connection) {
System.out.println(" [TIME_WAIT] 等待2MSL后关闭...");
connection.setState(new ClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println(" [TIME_WAIT] 收到最终确认");
}
@Override
public void sendData(TCPConnection connection, String data) {
System.out.println(" [TIME_WAIT] 无法发送数据");
}
@Override
public String getStateName() { return "TIME_WAIT"; }
}
// ========== 上下文 ==========
/**
* TCP 连接(上下文)
*/
class TCPConnection {
private String localAddress;
private String remoteAddress;
private TCPState currentState;
public TCPConnection(String local, String remote) {
this.localAddress = local;
this.remoteAddress = remote;
this.currentState = new ClosedState();
}
public void setState(TCPState state) {
System.out.println(" [状态转换] " + currentState.getStateName()
+ " -> " + state.getStateName());
this.currentState = state;
}
public void connect() { currentState.connect(this); }
public void disconnect() { currentState.disconnect(this); }
public void acknowledge() { currentState.acknowledge(this); }
public void sendData(String data) { currentState.sendData(this); }
public String getCurrentStateName() { return currentState.getStateName(); }
public void showStatus() {
System.out.println(" [TCP连接] " + localAddress + " <-> " + remoteAddress
+ " 状态: " + currentState.getStateName());
}
}
// ========== 客户端代码 ==========
public class TCPStateDemo {
public static void main(String[] args) {
TCPConnection connection = new TCPConnection("192.168.1.1:8080", "10.0.0.1:443");
System.out.println("===== TCP 三次握手 =====");
connection.showStatus();
connection.connect();
connection.acknowledge();
connection.acknowledge();
System.out.println("\n===== 数据传输 =====");
connection.sendData("Hello Server!");
connection.sendData("GET /index.html HTTP/1.1");
System.out.println("\n===== TCP 四次挥手 =====");
connection.disconnect();
connection.acknowledge();
System.out.println("\n===== 连接状态 =====");
connection.showStatus();
}
}六、状态转换表(有限状态机)
java
/**
* 状态转换表
* 用于可视化状态转换关系
*/
class StateTransitionTable {
private final Map<String, Map<String, String>> transitions = new HashMap<>();
public StateTransitionTable() {
// 订单状态转换表
addTransition("待支付", "支付", "已支付");
addTransition("待支付", "取消", "已取消");
addTransition("已支付", "发货", "已发货");
addTransition("已支付", "取消", "已取消");
addTransition("已支付", "退款", "已退款");
addTransition("已发货", "签收", "已签收");
addTransition("已签收", "退款", "已退款");
}
private void addTransition(String from, String action, String to) {
transitions.computeIfAbsent(from, k -> new HashMap<>()).put(action, to);
}
public String getNextState(String currentState, String action) {
Map<String, String> actions = transitions.get(currentState);
if (actions != null) {
return actions.get(action);
}
return null;
}
public void printTable() {
System.out.println(" +--------+--------+--------+");
System.out.println(" | 当前状态 | 操作 | 下一状态 |");
System.out.println(" +--------+--------+--------+");
for (Map.Entry<String, Map<String, String>> entry : transitions.entrySet()) {
for (Map.Entry<String, String> action : entry.getValue().entrySet()) {
System.out.printf(" | %-6s | %-6s | %-6s |%n",
entry.getKey(), action.getKey(), action.getValue());
}
}
System.out.println(" +--------+--------+--------+");
}
}
// ========== 使用示例 ==========
public class StateTableDemo {
public static void main(String[] args) {
StateTransitionTable table = new StateTransitionTable();
System.out.println("===== 状态转换表 =====");
table.printTable();
System.out.println("\n===== 模拟状态转换 =====");
String currentState = "待支付";
String[] actions = {"支付", "发货", "签收", "退款"};
for (String action : actions) {
String nextState = table.getNextState(currentState, action);
if (nextState != null) {
System.out.println(" " + currentState + " --[" + action + "]--> " + nextState);
currentState = nextState;
} else {
System.out.println(" " + currentState + " 无法执行 " + action);
}
}
}
}七、状态模式 vs 策略模式
java
/**
* 状态模式 vs 策略模式 的区别
*
* 相同点:
* 1. 都使用组合/委托模式
* 2. 都将行为封装到独立的类中
* 3. 都符合开闭原则
*
* 不同点:
* 1. 策略模式:客户端主动选择策略,策略之间不知道彼此的存在
* 2. 状态模式:状态对象决定状态转换,状态之间知道彼此的存在
*
* 策略模式:
* - 客户端选择支付方式(支付宝、微信、银行卡)
* - 支付方式之间没有关系,客户端决定用哪个
*
* 状态模式:
* - 订单状态自动转换(待支付 -> 已支付 -> 已发货)
* - 状态之间有转换关系,当前状态决定下一步转换
*/八、UML 类图
┌───────────────────┐ ┌──────────────────┐
│ Client │ │ Context │
└─────────┬─────────┘ │ -state: State │
│ │ +request() │
▼ └────────┬─────────┘
┌───────────────────┐ │ 持有
│ State (接口) │◀─────────────┘
│ +handle() │
└────────┬──────────┘
│ 实现
┌────┴────┐
│ │
┌───┴───┐ ┌───┴───┐ ┌───────┐
│State A│ │State B│ │State C│
│+handle│ │+handle│ │+handle│
└───────┘ └───────┘ └───────┘
│ │ │
└────next─┘────next─┘九、优缺点分析
优点
- 消除 if-else:将状态相关的行为分散到各个状态类中,避免了大量的条件判断
- 单一职责:每个状态类只关注自己状态下的行为
- 符合开闭原则:新增状态不需要修改现有代码
- 状态转换清晰:状态转换逻辑集中在状态类中,易于理解和维护
- 状态与行为绑定:不同状态下的行为自然分离
缺点
- 类数量增加:每个状态都需要一个具体的状态类
- 状态转换复杂:状态之间的转换关系可能很复杂
- 过度设计:对于简单的状态转换(2-3个状态),使用状态模式可能过度设计
十、使用场景
- 订单系统:订单的创建、支付、发货、收货等状态流转
- 游戏角色:角色的各种状态(站立、行走、攻击等)
- TCP/IP 协议:TCP 连接的各种状态
- 工作流引擎:审批流程的各个状态
- 媒体播放器:播放、暂停、停止等状态
- 自动售货机:空闲、投币、选择、出货等状态
- 线程状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
十一、状态转换表的实现
java
/**
* 使用状态转换表简化状态模式
* 适用于状态转换逻辑简单的场景
*/
class StateMachine<S, A> {
private final Map<S, Map<A, S>> transitions = new HashMap<>();
private S currentState;
public StateMachine(S initialState) {
this.currentState = initialState;
}
public StateMachine<S, A> addTransition(S from, A action, S to) {
transitions.computeIfAbsent(from, k -> new HashMap<>()).put(action, to);
return this;
}
public boolean trigger(A action) {
Map<A, S> actions = transitions.get(currentState);
if (actions != null && actions.containsKey(action)) {
S previousState = currentState;
currentState = actions.get(action);
System.out.println(" " + previousState + " --[" + action + "]--> " + currentState);
return true;
}
System.out.println(" " + currentState + " 无法执行 " + action);
return false;
}
public S getCurrentState() { return currentState; }
public void reset() {
// 需要记住初始状态
}
}
// ========== 使用示例 ==========
public class StateMachineDemo {
public static void main(String[] args) {
StateMachine<String, String> orderFSM = new StateMachine<>("待支付");
orderFSM.addTransition("待支付", "支付", "已支付")
.addTransition("待支付", "取消", "已取消")
.addTransition("已支付", "发货", "已发货")
.addTransition("已支付", "退款", "已退款")
.addTransition("已发货", "签收", "已签收")
.addTransition("已签收", "退款", "已退款");
System.out.println("===== 订单状态机 =====");
orderFSM.trigger("支付");
orderFSM.trigger("发货");
orderFSM.trigger("签收");
orderFSM.trigger("退款");
}
}十二、最佳实践
- 合理划分状态:不要过度细分状态,也不要合并太多行为到一个状态
- 明确状态转换条件:每个状态都应该清楚地知道可以转换到哪些状态
- 使用状态转换表:对于复杂的状态转换,使用状态转换表可视化
- 考虑使用状态机框架:对于复杂的状态逻辑,可以使用 Spring StateMachine 等框架
- 避免循环转换:注意防止状态之间的无限循环
java
// 推荐:使用枚举实现简单状态
enum OrderStatus {
PENDING {
@Override public void onPay(Order order) {
order.setStatus(PAID);
}
},
PAID {
@Override public void onShip(Order order) {
order.setStatus(SHIPPED);
}
};
public abstract void onPay(Order order);
public abstract void onShip(Order order);
}总结:状态模式的核心是"将状态封装为对象"。它通过将每个状态封装成独立的类,避免了大量的 if-else 判断,使代码更加清晰和可维护。在实际应用中,订单系统、游戏角色、TCP 连接等都是状态模式的典型应用场景。选择状态模式还是策略模式,取决于状态转换是自动的(状态模式)还是由客户端控制的(策略模式)。