Skip to content

状态模式(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─┘

九、优缺点分析 ​

优点 ​

  1. 消除 if-else:将状态相关的行为分散到各个状态类中,避免了大量的条件判断
  2. 单一职责:每个状态类只关注自己状态下的行为
  3. 符合开闭原则:新增状态不需要修改现有代码
  4. 状态转换清晰:状态转换逻辑集中在状态类中,易于理解和维护
  5. 状态与行为绑定:不同状态下的行为自然分离

缺点 ​

  1. 类数量增加:每个状态都需要一个具体的状态类
  2. 状态转换复杂:状态之间的转换关系可能很复杂
  3. 过度设计:对于简单的状态转换(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("退款");
    }
}

十二、最佳实践 ​

  1. 合理划分状态:不要过度细分状态,也不要合并太多行为到一个状态
  2. 明确状态转换条件:每个状态都应该清楚地知道可以转换到哪些状态
  3. 使用状态转换表:对于复杂的状态转换,使用状态转换表可视化
  4. 考虑使用状态机框架:对于复杂的状态逻辑,可以使用 Spring StateMachine 等框架
  5. 避免循环转换:注意防止状态之间的无限循环
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 连接等都是状态模式的典型应用场景。选择状态模式还是策略模式,取决于状态转换是自动的(状态模式)还是由客户端控制的(策略模式)。