Java 编程之状态模式

一、引言

在电梯运行的模型中,你是否考虑过这样写

java 复制代码
if (state.equals("运行")) {
    // 忽略开门请求
} else if (state.equals("停止")) {
    // 执行开门
} else if (state.equals("开门")) {
    // 等待用户进入
}

你可能只是在控制一个简单的"电梯",但这段代码告诉你:

  • 一堆 if-else状态和行为耦合死了

  • 状态变多,逻辑混乱,难以维护

  • 改功能=地狱级维护

    那该怎么解决这个问题呢?就用今天我们要讨论的状态模式最恰当不过了。


二、状态模式(State Pattern)

定义:将对象的状态封装成独立类,状态之间可以互相切换,对象在不同状态下表现出不同行为。

通俗讲:让对象的行为"随着状态而改变",不再由 if-else 控制,而由"状态类"自己说了算!

举一个生活中的例子:电梯的行为受"状态"决定

电梯状态 = 它现在在哪一层 + 开没开门 + 是否在运行

状态 vs 行为对照表:

当前状态 用户按"开门"
开门状态 ❗门已打开
关门+停止状态 ✅ 打开门
运行状态 🚫 无法开门

四、为什么状态模式更适合

假设我们用状态模式重写电梯逻辑,代码看起来像这样:

java 复制代码
elevator.open();  // 当前状态是"停止",所以可以打开门
elevator.run();   // 当前是"关门",可以运行
elevator.open();  // 当前是"运行中",无法开门

每个状态都封装在独立类中,电梯对象只管当前处于什么状态,行为交由状态类处理。代码简介,逻辑清晰。


五、状态模式类图

plantuml 复制代码
@startuml
interface LiftState {
  +open(): void
  +close(): void
  +run(): void
  +stop(): void
}

class OpenState implements LiftState
class RunningState implements LiftState
class StoppedState implements LiftState

class Elevator {
  -state: LiftState
  +setState(s: LiftState)
  +open()
  +close()
  +run()
  +stop()
}

Elevator --> LiftState
LiftState <|.. OpenState
LiftState <|.. RunningState
LiftState <|.. StoppedState
@enduml

六、完整 Java 实战代码

1. 状态接口

java 复制代码
public interface LiftState {
    void open(Elevator context);
    void close(Elevator context);
    void run(Elevator context);
    void stop(Elevator context);
}

2. 三种具体状态类(开门、运行、停止)

开门状态

java 复制代码
public class OpenState implements LiftState {
    public void open(Elevator context) {
        System.out.println("门已经开着");
    }
    public void close(Elevator context) {
        System.out.println("门关闭了");
        context.setState(new StoppedState());
    }
    public void run(Elevator context) {
        System.out.println("🚫 开着门不能运行");
    }
    public void stop(Elevator context) {
        System.out.println("电梯停止中");
    }
}

运行状态

java 复制代码
public class RunningState implements LiftState {
    public void open(Elevator context) {
        System.out.println("🚫 运行中不能开门");
    }
    public void close(Elevator context) {
        System.out.println("门已经关着");
    }
    public void run(Elevator context) {
        System.out.println("电梯正在运行中...");
    }
    public void stop(Elevator context) {
        System.out.println("运行结束,电梯停止");
        context.setState(new StoppedState());
    }
}

停止状态

java 复制代码
public class StoppedState implements LiftState {
    public void open(Elevator context) {
        System.out.println("打开门");
        context.setState(new OpenState());
    }
    public void close(Elevator context) {
        System.out.println("门已经关闭");
    }
    public void run(Elevator context) {
        System.out.println("开始运行");
        context.setState(new RunningState());
    }
    public void stop(Elevator context) {
        System.out.println("已经停止");
    }
}

3. 电梯本体类

java 复制代码
public class Elevator {
    private LiftState state;

    public Elevator() {
        this.state = new StoppedState(); // 默认初始状态
    }

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

    public void open() {
        state.open(this);
    }

    public void close() {
        state.close(this);
    }

    public void run() {
        state.run(this);
    }

    public void stop() {
        state.stop(this);
    }
}

4. 测试代码

java 复制代码
public class Client {
    public static void main(String[] args) {
        Elevator lift = new Elevator();

        lift.open();   // 打开门(从停止)
        lift.close();  // 关门(切回停止)
        lift.run();    // 开始运行
        lift.open();   // 运行中不能开门
        lift.stop();   // 停止
        lift.open();   // 打开门
    }
}

5. 代码输出

复制代码
打开门
门关闭了
开始运行
🚫 运行中不能开门
运行结束,电梯停止
打开门

是不是比 if-else 的实现清晰太多?


七、总结

同一个对象,不同状态下表现出不同行为,行为被"状态对象"接管,不靠或减少if else判断语句。

优势 描述
✅ 可扩展 新增状态只需加类,不动旧代码
✅ 可读性强 不再靠状态变量判断,行为清晰
✅ 分工明确 每个状态类只负责自己状态下该干嘛
🚫 类增多 状态太多时类数量多,但逻辑不乱

适用场景举例

  • 电梯状态管理(运行/停止/开门)
  • 游戏角色行为(攻击/防御/死亡)
  • 表单状态(草稿/审核/发布)
  • 网络连接状态(连接中/已连接/断开)
  • 审批流程节点状态(待审/驳回/通过)

八、参考

《23种设计模式概览》

相关推荐
hqxstudying5 分钟前
Intellij IDEA中Maven的使用
java·maven·intellij-idea
SimonKing8 分钟前
拯救大文件上传:一文彻底彻底搞懂秒传、断点续传以及分片上传
java·后端·架构
深栈解码8 分钟前
JUC并发编程 内存布局和对象头
java·后端
北方有星辰zz22 分钟前
数据结构:栈
java·开发语言·数据结构
Seven9723 分钟前
一个static关键字引发的线上故障:深度剖析静态变量与配置热更新的陷阱
java
山野万里__25 分钟前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
风象南26 分钟前
Spring Shell命令行工具开发实战
java·spring boot·后端
Java技术小馆31 分钟前
POST为什么发送两次请求
java·面试·架构
天天摸鱼的java工程师32 分钟前
MySQL表设计实战指南:从业务场景到表结构优化
java·后端·mysql
SimonKing34 分钟前
Java处理PDF就靠它!Apache PDFBox:开源免费的PDF全能王
java·后端·程序员