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种设计模式概览》

相关推荐
郝学胜-神的一滴6 分钟前
Effective STL 第1条:慎重选择容器类型
开发语言·c++·程序人生·软件工程
熊小猿14 分钟前
ArrayList 与 LinkedList 的区别
java·面试
阿明619 分钟前
list模拟实现(简单版)【C++】
开发语言·c++·学习·list
Yupureki33 分钟前
从零开始的C++学习生活 1:命名空间,缺省函数,函数重载,引用,内联函数
c语言·开发语言·c++·学习·visual studio
鄃鳕1 小时前
高并发日志项目中,C++IO的使用
开发语言·c++
笨手笨脚の1 小时前
设计模式-装饰器模式
java·设计模式·装饰器模式·结构型设计模式
点云侠1 小时前
PCL 生成缺角立方体点云
开发语言·c++·人工智能·算法·计算机视觉
9毫米的幻想1 小时前
【Linux系统】—— 程序地址空间
java·linux·c语言·jvm·c++·学习
C++chaofan1 小时前
Redisson分布式限流
java·jvm·spring boot·redis·分布式·mvc·redisson
whltaoin1 小时前
Java 网络请求 Jar 包选型指南:从基础到实战
java·http·okhttp·网络请求·retrofit