一、状态模式(State Pattern)

1. 模式概述

状态模式 是一种 行为型设计模式,它允许一个对象在其内部状态改变时 改变其行为,使得对象看起来像是修改了它的类。

核心思想:“状态即对象” —— 把每个状态封装成独立的类,让状态自己决定能做什么、不能做什么。

状态模式 = 状态驱动行为 + 自动流转

  • 它不是“替换 if-else”,而是“用对象表达状态”。

类比理解:电梯系统:

  • 门开着时不能运行;运行中不能开门;停止后才能开门……
  • 每种状态下的操作规则完全不同。

2. 使用场景

状态模式适用于以下情况:

  • 对象的行为 依赖于它的状态,且必须在运行时根据状态动态改变;
  • 代码中存在 大量与状态相关的条件判断(如 if/else 或 switch);
  • 状态转换逻辑 复杂且易变,需要解耦;
  • 希望 避免状态判断散落在多个方法中,导致维护困难。

典型应用:

  • 订单状态机(待支付 → 已支付 → 已发货 → 已完成);
  • 游戏角色状态(站立、奔跑、跳跃、攻击);
  • 工作流引擎(草稿 → 审核中 → 已发布 → 已归档);
  • 网络连接状态(断开 → 连接中 → 已连接 → 重连)。

在实际开发中:

  • 凡是涉及 有限状态机(FSM) 的场景,优先考虑状态模式;
  • 订单、审批、设备控制、游戏 AI 都是经典用例;
  • 当你发现 switch-case 越来越长,就是重构的好时机。

3. 示例程序:电梯控制系统(优化版)

1)状态接口 LiftState

  • 默认抛出 有意义的异常,而非 RuntimeException;
  • 子类只需重写 允许的操作,其他保持默认。
/**
 * 状态接口(State)
 */
public abstract class LiftState {
    protected Elevator elevator;

    public void setElevator(Elevator elevator) {
        this.elevator = elevator;
    }

    // 所有状态必须实现的操作(可选择性抛出 IllegalStateException)
    public void open() { throw new IllegalStateException("当前状态下不允许开门"); }
    public void close() { throw new IllegalStateException("当前状态下不允许关门"); }
    public void run() { throw new IllegalStateException("当前状态下不允许运行"); }
    public void stop() { throw new IllegalStateException("当前状态下不允许停止"); }
}

2)具体状态类

a、开启状态

public class OpeningState extends LiftState {
    @Override
    public void open() {
        System.out.println("【开门】电梯门已开启");
    }

    @Override
    public void close() {
        System.out.println("【关门】从开启状态切换到关闭状态");
        elevator.setState(new ClosingState());
        elevator.close(); // 触发新状态的 close 行为
    }
}

b、关闭状态

public class ClosingState extends LiftState {
    @Override
    public void close() {
        System.out.println("【关门】电梯门已关闭");
    }

    @Override
    public void open() {
        System.out.println("【开门】从关闭状态切换到开启状态");
        elevator.setState(new OpeningState());
        elevator.open();
    }

    @Override
    public void run() {
        System.out.println("【运行】从关闭状态切换到运行状态");
        elevator.setState(new RunningState());
        elevator.run();
    }

    @Override
    public void stop() {
        System.out.println("【停止】从关闭状态切换到停止状态");
        elevator.setState(new StoppingState());
        elevator.stop();
    }
}

3)运行状态

public class RunningState extends LiftState {
    @Override
    public void run() {
        System.out.println("【运行】电梯正在运行中……");
    }

    @Override
    public void stop() {
        System.out.println("【停止】从运行状态切换到停止状态");
        elevator.setState(new StoppingState());
        elevator.stop();
    }
    // open/close 保持默认异常
}

4)停止状态

public class StoppingState extends LiftState {
    @Override
    public void stop() {
        System.out.println("【停止】电梯已停止");
    }

    @Override
    public void open() {
        System.out.println("【开门】从停止状态切换到开启状态");
        elevator.setState(new OpeningState());
        elevator.open();
    }

    @Override
    public void run() {
        System.out.println("【运行】从停止状态切换到运行状态");
        elevator.setState(new RunningState());
        elevator.run();
    }
    // close 保持默认异常
}

3)环境类 Elevator

/**
 * 环境类(Context)
 */
public class Elevator {
    private LiftState state;

    public Elevator() {
        // 初始状态设为关闭
        setState(new ClosingState());
    }

    public void setState(LiftState state) {
        this.state = state;
        this.state.setElevator(this);
    }

    // 委托给当前状态
    public void open() { state.open(); }
    public void close() { state.close(); }
    public void run() { state.run(); }
    public void stop() { state.stop(); }
}

4)客户端测试 Main

public class Main {
    public static void main(String[] args) {
        Elevator elevator = new Elevator();

        elevator.open();   // 关闭 → 开启
        System.out.println("--------");
        elevator.close();  // 开启 → 关闭
        System.out.println("--------");
        elevator.run();    // 关闭 → 运行
        System.out.println("--------");
        elevator.stop();   // 运行 → 停止
        System.out.println("--------");

        // 尝试非法操作:运行中开门(会抛异常)
        try {
            elevator.run(); // 停止 → 运行
            elevator.open(); // ❌ 运行中不能开门
        } catch (IllegalStateException e) {
            System.out.println("❌ 操作失败: " + e.getMessage());
        }
    }
}

输出:

【开门】从关闭状态切换到开启状态
【开门】电梯门已开启
--------
【关门】从开启状态切换到关闭状态
【关门】电梯门已关闭
--------
【运行】从关闭状态切换到运行状态
【运行】电梯正在运行中……
--------
【停止】从运行状态切换到停止状态
【停止】电梯已停止
--------
【运行】从停止状态切换到运行状态
【运行】电梯正在运行中……
❌ 操作失败: 当前状态下不允许开门

5)模式角色

  • 所有状态行为 由具体状态类实现;
  • 状态切换 由状态自身或 Context 控制;
  • 客户端 只与 Context 交互,无需知道具体状态。
角色 职责 示例
Context(环境类) 持有当前状态引用,对外提供统一接口,将请求委托给当前状态对象 Elevator / ProductContext
State(状态接口) 定义状态相关行为的公共接口 LiftState
ConcreteState(具体状态类) 实现特定状态下的行为逻辑,可触发状态切换 OpeningState, RunningState

4. FQA

1)状态模式 vs 条件分支

维度 条件分支 状态模式
可读性 低(逻辑分散) 高(每个状态职责单一)
可维护性 差(新增状态需改多处) 优(新增状态类即可)
扩展性 困难 极易
封装性 无 高(状态行为内聚)
适用规模 状态少、逻辑简单 状态多、转换复杂

2)模式优点

  • 消除庞大的条件语句,代码更清晰;
  • 符合开闭原则:新增状态无需修改现有代码;
  • 状态行为高内聚,易于测试和复用;
  • 状态转换逻辑集中,便于审计和修改。

3)注意事项

  • 类数量增加:每个状态需一个类;
  • 状态间可能相互依赖,需谨慎设计转换规则;
  • 不适合状态极少或转换简单的场景(过度设计)。