设计模式详解-状态模式

状态模式(State Pattern)

超级通俗一句话 :把对象的不同状态 封装成独立的状态类 ,让对象在状态变化时,自动切换行为逻辑,就像电梯运行 ------电梯有「停止、上升、下降」3种状态,按开门键时,停止状态 会开门,上升/下降状态 则忽略操作,状态决定行为 ,而非一堆 if/else 判断。

核心是用"状态类"替代"状态判断分支" ,解决大量 if-else/switch 导致的代码臃肿、难以维护问题,这也是它和其他行为型模式最核心的区别。


一、核心特点(一眼记住)

  1. 状态与行为绑定:不同状态对应不同行为,状态变,行为自动变,无需手动判断;
  2. 状态封装独立 :每个状态都是一个独立的类,符合单一职责,新增/修改状态不影响其他状态;
  3. 对象状态自动化:上下文对象(如电梯)持有当前状态,状态间的切换可由状态类自身控制(如电梯上升到目标楼层后,自动切换为停止状态);
  4. 消除分支判断 :彻底替代 if/else/switch,让代码更简洁、易扩展(符合开闭原则)。

项目中常见场景

  • 有限状态机(FSM):如电梯、订单、游戏角色状态;
  • 业务流程状态流转:如订单「待支付→已支付→待发货→已发货→已完成」;
  • 设备状态控制:如空调「制冷→制热→送风→关机」;
  • 游戏开发:如角色「正常→受伤→死亡→复活」。

二、标准结构(3个核心角色,极简且固定)

状态模式的结构是所有行为型模式中最简洁的之一,3个角色各司其职,无冗余,这也是它易落地、易维护的原因:

  1. State(抽象状态) :定义所有状态的统一行为接口,包含该状态下要执行的方法,同时可定义状态切换的通用方法;
  2. ConcreteState(具体状态) :实现抽象状态接口,封装当前状态下的具体行为 ,并负责状态的切换逻辑(决定何时切换到哪个状态);
  3. Context(上下文)持有当前状态对象,是外部交互的入口,对外提供统一的业务方法,实际行为由当前状态对象执行,自身不包含状态判断逻辑。

角色关系梳理
上下文 → 持有当前具体状态 → 外部调用上下文的方法 → 上下文委托当前状态执行具体行为 → 具体状态执行行为并决定是否切换到新状态上下文更新当前状态


三、Java代码实战(最简单版本,电梯案例)

电梯运行 的经典场景实现,完美贴合3个角色,彻底替代 if/else,一看就懂,代码无冗余!

业务场景:电梯有3种核心状态,行为严格绑定状态

  • 停止状态:可开门、可按楼层(切换为上升/下降);
  • 上升状态:不可开门、到达目标楼层后自动切换为停止状态;
  • 下降状态:不可开门、到达目标楼层后自动切换为停止状态。

1. State(抽象状态:电梯状态)

定义电梯所有状态的统一行为接口:开门、运行、停止。

java 复制代码
// 抽象状态:电梯状态
public interface ElevatorState {
    // 开门操作
    void openDoor();
    // 运行操作(上升/下降)
    void run();
    // 停止操作
    void stop();
}

2. ConcreteState(具体状态:3种电梯状态,封装行为+状态切换)

每个状态类只实现自己的行为,不符合当前状态的行为直接做友好提示,并负责状态切换逻辑。

① 停止状态(电梯静止时)
java 复制代码
// 具体状态1:停止状态
public class StopState implements ElevatorState {
    // 持有上下文:用于切换状态
    private ElevatorContext context;

    public StopState(ElevatorContext context) {
        this.context = context;
    }

    @Override
    public void openDoor() {
        // 停止状态:可开门
        System.out.println("电梯【停止状态】:开门成功,请上下楼!");
    }

    @Override
    public void run() {
        // 停止状态:运行(切换为上升/下降,这里简化为直接切换上升)
        System.out.println("电梯【停止状态】:开始运行,切换为上升状态!");
        context.setCurrentState(context.getUpState());
    }

    @Override
    public void stop() {
        // 停止状态:再次停止,无操作
        System.out.println("电梯【停止状态】:已静止,无需重复停止!");
    }
}
② 上升状态(电梯上升时)
java 复制代码
// 具体状态2:上升状态
public class UpState implements ElevatorState {
    private ElevatorContext context;

    public UpState(ElevatorContext context) {
        this.context = context;
    }

    @Override
    public void openDoor() {
        // 上升状态:不可开门
        System.out.println("电梯【上升状态】:运行中禁止开门,危险!");
    }

    @Override
    public void run() {
        // 上升状态:持续运行,无操作
        System.out.println("电梯【上升状态】:正在上升,目标楼层5楼!");
    }

    @Override
    public void stop() {
        // 上升状态:到达楼层,停止并切换为停止状态
        System.out.println("电梯【上升状态】:到达目标楼层,停止运行,切换为停止状态!");
        context.setCurrentState(context.getStopState());
    }
}
③ 下降状态(电梯下降时)
java 复制代码
// 具体状态3:下降状态
public class DownState implements ElevatorState {
    private ElevatorContext context;

    public DownState(ElevatorContext context) {
        this.context = context;
    }

    @Override
    public void openDoor() {
        // 下降状态:不可开门
        System.out.println("电梯【下降状态】:运行中禁止开门,危险!");
    }

    @Override
    public void run() {
        // 下降状态:持续运行,无操作
        System.out.println("电梯【下降状态】:正在下降,目标楼层1楼!");
    }

    @Override
    public void stop() {
        // 下降状态:到达楼层,停止并切换为停止状态
        System.out.println("电梯【下降状态】:到达目标楼层,停止运行,切换为停止状态!");
        context.setCurrentState(context.getStopState());
    }
}

3. Context(上下文:电梯)

外部交互的唯一入口,持有所有状态对象和当前状态,对外提供统一方法,自身不做任何业务逻辑,全部委托给当前状态。

java 复制代码
// 上下文:电梯(外部唯一交互入口)
public class ElevatorContext {
    // 定义所有状态对象(单例,避免重复创建)
    private final ElevatorState stopState;
    private final ElevatorState upState;
    private final ElevatorState downState;
    // 持有当前状态(核心)
    private ElevatorState currentState;

    // 构造器:初始化所有状态,默认初始状态为停止
    public ElevatorContext() {
        this.stopState = new StopState(this);
        this.upState = new UpState(this);
        this.downState = new DownState(this);
        this.currentState = stopState;
    }

    // 对外提供的统一方法:开门
    public void openDoor() {
        currentState.openDoor();
    }

    // 对外提供的统一方法:运行
    public void run() {
        currentState.run();
    }

    // 对外提供的统一方法:停止
    public void stop() {
        currentState.stop();
    }

    // 状态切换的setter/getter
    public void setCurrentState(ElevatorState currentState) {
        this.currentState = currentState;
    }

    public ElevatorState getStopState() { return stopState; }
    public ElevatorState getUpState() { return upState; }
    public ElevatorState getDownState() { return downState; }
}

4. 测试使用(外部只与上下文交互,无需关心状态)

java 复制代码
// 测试类:外部调用者
public class StateTest {
    public static void main(String[] args) {
        // 1. 创建上下文:电梯
        ElevatorContext elevator = new ElevatorContext();
        System.out.println("===== 初始状态:停止 =====");
        // 2. 调用开门(停止状态可开门)
        elevator.openDoor();
        // 3. 调用运行(停止状态→上升状态)
        elevator.run();
        System.out.println("===== 切换后状态:上升 =====");
        // 4. 上升状态尝试开门(禁止)
        elevator.openDoor();
        // 5. 上升状态持续运行
        elevator.run();
        // 6. 上升状态停止(→停止状态)
        elevator.stop();
        System.out.println("===== 切换后状态:停止 =====");
        // 7. 再次开门(可开门)
        elevator.openDoor();
    }
}

运行结果(状态自动切换,行为严格绑定状态)

复制代码
===== 初始状态:停止 =====
电梯【停止状态】:开门成功,请上下楼!
电梯【停止状态】:开始运行,切换为上升状态!
===== 切换后状态:上升 =====
电梯【上升状态】:运行中禁止开门,危险!
电梯【上升状态】:正在上升,目标楼层5楼!
电梯【上升状态】:到达目标楼层,停止运行,切换为停止状态!
===== 切换后状态:停止 =====
电梯【停止状态】:开门成功,请上下楼!

四、扩展:新增状态(零改动,符合开闭原则)

如果想给电梯加故障状态 (所有操作都提示故障),只需新增一个具体状态类 ,修改上下文的初始化代码,无需改动任何原有状态类和业务逻辑,这就是状态模式的核心优势:

1. 新增具体状态:故障状态

java 复制代码
// 新增具体状态:故障状态
public class FaultState implements ElevatorState {
    public FaultState(ElevatorContext context) {
        // 无需持有上下文,故障状态不切换其他状态
    }

    @Override
    public void openDoor() {
        System.out.println("电梯【故障状态】:设备故障,无法开门!");
    }

    @Override
    public void run() {
        System.out.println("电梯【故障状态】:设备故障,无法运行!");
    }

    @Override
    public void stop() {
        System.out.println("电梯【故障状态】:已故障停机,请联系维修!");
    }
}

2. 上下文新增故障状态(仅修改此处)

java 复制代码
// 电梯上下文新增故障状态
public class ElevatorContext {
    private final ElevatorState faultState; // 新增故障状态
    // 其他原有状态不变...

    public ElevatorContext() {
        // 原有初始化不变...
        this.faultState = new FaultState(this); // 初始化故障状态
        this.currentState = stopState;
    }

    // 新增:切换故障状态的方法
    public void fault() {
        System.out.println("电梯:发生故障,切换为故障状态!");
        this.currentState = faultState;
    }

    // 新增getter...
    public ElevatorState getFaultState() { return faultState; }
}

3. 外部调用新增状态(无侵入式)

java 复制代码
// 测试新增故障状态
elevator.fault(); // 切换为故障状态
elevator.openDoor(); // 故障状态无法开门
elevator.run(); // 故障状态无法运行

运行结果

复制代码
电梯:发生故障,切换为故障状态!
电梯【故障状态】:设备故障,无法开门!
电梯【故障状态】:设备故障,无法运行!

五、和前4种模式的核心区别(一张表秒懂)

你已经学了代理、装饰器、责任链、命令、状态 5种行为型模式,均易混淆,用核心目的+核心特征 做终极对比,彻底区分,结合你的麻将游戏项目,一眼就能判断该用哪种:

模式 核心目的 核心特征 典型场景 麻将项目适用场景
代理模式 控制访问(替身) 代理持有一个真实对象 权限、远程调用、懒加载 玩家操作权限校验、远程房间调用
装饰器模式 增强功能(套娃) 装饰器持有一个组件,层层包装 IO流、日志/缓存增强 玩家出牌日志、牌型计算增强
责任链模式 请求流转(谁能处理谁处理) 处理器持有下一个处理器,串链 审批流、过滤器 出牌合法性校验链(牌型→规则→次数)
命令模式 解耦请求与执行(封装操作) 命令持有一个接收者,调用者持命令 按钮操作、撤销/恢复 打牌/碰牌/杠牌操作封装、撤销
状态模式 状态决定行为(消除分支) 上下文持有当前状态,状态封装行为 有限状态机、状态流转 房间/游戏状态控制(待开局→游戏中→游戏结束)

一句话极简区分

  • 代理:替你做(控制访问);
  • 装饰器:给你加功能(套娃增强);
  • 责任链:顺着链条传(分发处理);
  • 命令:封装起来让别人做(解耦请求与执行);
  • 状态:什么状态做什么事(状态绑定行为,消除if/else)。

六、状态模式的核心优势&劣势&适用场景

核心优势

  1. 消除分支判断 :彻底替代 if/else/switch,解决代码臃肿、难以维护的问题(这是最核心的价值);
  2. 状态封装独立:每个状态都是独立类,符合单一职责,新增/修改状态无侵入,易扩展;
  3. 状态逻辑清晰:状态的行为和切换逻辑都在对应状态类中,便于调试和维护;
  4. 行为自动化:状态切换由状态类自身控制,上下文无需关心,降低耦合。

轻微劣势

  1. 类数量增加:每个状态对应一个类,状态较多时会产生一定的类数量(但相比混乱的if/else,这是可接受的代价);
  2. 状态切换复杂时需注意 :若状态间切换逻辑过于复杂,可能会导致状态类之间产生耦合(可通过状态管理器优化)。

最佳适用场景

当对象的行为由其状态决定,且存在大量状态判断分支时,优先使用状态模式,具体为:

  1. 对象有有限的、明确的状态(如订单5种状态、电梯3种状态);
  2. 不同状态下行为差异明显 ,且状态间有明确的切换规则
  3. 代码中存在大量 if/else/switch 判断状态,且维护成本高。

七、超级总结(记住3点,落地无忧)

  1. 核心思想状态决定行为,将每个状态封装为独立类,让对象的行为随状态自动变化;
  2. 关键特征 :3个核心角色,上下文持有当前状态状态类封装行为和切换逻辑,外部只与上下文交互;
  3. 核心价值消除大量分支判断,让状态相关的代码更简洁、易扩展、易维护,是实现**有限状态机(FSM)**的最佳设计模式。

代理:替你做,控制访问;

装饰器:给你加功能,套娃增强;

责任链:顺着链条传,谁能处理谁处理;

命令:封装操作,解耦请求与执行,可撤销;

策略:外部选算法,算法可互换;

状态:内部自动切换状态,状态决定行为,消除 if/else。

相关推荐
han_2 小时前
JavaScript设计模式(七):迭代器模式实现与应用
前端·javascript·设计模式
xy34533 小时前
Axure 9.0 原生组件:让折线图实现动态交互(文本标签)
ui·交互·axure·原型·折线图
前端不太难11 小时前
智能体可信之路:全链路安全防御
安全·状态模式·openclaw
智算菩萨13 小时前
【Tkinter】4 Tkinter Entry 输入框控件深度解析:数据验证、密码输入与现代表单设计实战
python·ui·tkinter·数据验证·entry·输入框
大数据新鸟16 小时前
设计模式详解——观察者模式
观察者模式·设计模式
今天也要学习吖16 小时前
开源AI智能客服系统AI-CS
人工智能·ui·chatgpt·golang·开源·gemini·智能客服系统
武藤一雄18 小时前
C# 设计模式大全(第一弹|7种)
microsoft·设计模式·微软·c#·.net·.netcore
多看书少吃饭20 小时前
Vue3 + Java + Python 打造企业级大模型知识库(含 SSE 流式对话完整源码)
java·python·状态模式
UI设计兰亭妙微20 小时前
兰亭妙微原创解读:《术与道》——移动应用UI设计的体系化知识框架
ui·b端界面设计·ui设计公司