一、状态模式(State Pattern

1. 模式概述

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

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

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

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

类比理解:电梯系统

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

2. 使用场景

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

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

典型应用

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

在实际开发中:

  • 凡是涉及 有限状态机(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)注意事项

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