状态模式

状态模式:封装状态变化的行为设计艺术

一、状态模式的定义与核心思想

状态模式(State Pattern) 是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其核心思想是 “将对象的状态封装为独立的状态类,将状态转换逻辑与状态对应的行为分离”—— 通过将不同状态下的行为封装到具体状态类中,让上下文对象(持有状态的对象)在状态变化时,自动切换对应的行为,无需在上下文类中编写大量条件判断语句。

简单来说,状态模式解决了 “对象因状态变化导致行为切换,进而产生大量条件判断” 的问题。例如,电梯的运行状态(停止、上升、下降)、订单的生命周期(待支付、已支付、已发货、已完成)、播放器的状态(播放、暂停、停止)等场景,都适合用状态模式封装状态变化与对应行为。

二、状态模式的角色构成

状态模式通常包含以下 3 个核心角色,各角色职责明确、协作完成状态切换与行为执行:

角色名称 核心职责 示例(电梯运行场景)
上下文(Context) 持有当前状态对象,提供状态切换的接口,对外暴露统一的行为方法(委托给当前状态对象执行) Elevator 类(电梯)
抽象状态(State) 定义所有具体状态的公共接口,声明状态对应的行为方法(如电梯的开门、关门、运行方法) ElevatorState 接口
具体状态(ConcreteState) 实现抽象状态接口,封装该状态下的具体行为,同时可包含状态转换的逻辑(如从 “停止” 状态切换到 “上升” 状态) StoppedState(停止状态)、RunningUpState(上升状态)、RunningDownState(下降状态)

三、状态模式的工作原理与实现

1. 工作流程

  1. 上下文对象初始化时,设置初始状态(如电梯初始状态为 “停止”);

  2. 客户端调用上下文的行为方法(如电梯的 “上升” 方法);

  3. 上下文对象将行为委托给当前持有的状态对象执行;

  4. 具体状态对象执行对应行为,若行为触发状态变化(如电梯从 “停止” 变为 “上升”),则由状态对象通知上下文切换到新状态;

  5. 后续客户端调用上下文行为时,将委托给新的状态对象执行。

2. 基础实现(电梯运行状态管理)

以电梯的核心状态(停止、上升、下降)为例,实现状态模式,封装不同状态下的行为与状态转换:

(1)抽象状态(State)
// 抽象状态:电梯状态接口,定义电梯的核心行为

public interface ElevatorState {

   void openDoor(); // 开门

   void closeDoor(); // 关门

   void runUp(); // 上升

   void runDown(); // 下降

   void stop(); // 停止

}
(2)上下文(Context)
// 上下文:电梯类,持有当前状态,对外暴露统一行为接口

public class Elevator {

   // 定义所有可能的状态(也可通过工厂模式创建,此处简化)

   public final ElevatorState STOPPED_STATE = new StoppedState(this);

   public final ElevatorState RUNNING_UP_STATE = new RunningUpState(this);

   public final ElevatorState RUNNING_DOWN_STATE = new RunningDownState(this);

   private ElevatorState currentState; // 当前状态

   // 初始化:电梯默认处于停止状态

   public Elevator() {

       this.currentState = STOPPED_STATE;

       System.out.println("电梯初始状态:停止");

   }

   // 状态切换方法:由具体状态对象调用,切换当前状态

   public void setState(ElevatorState state) {

       this.currentState = state;

   }

   // 对外暴露的行为方法:委托给当前状态执行

   public void openDoor() {

       currentState.openDoor();

   }

   public void closeDoor() {

       currentState.closeDoor();

   }

   public void runUp() {

       currentState.runUp();

   }

   public void runDown() {

       currentState.runDown();

   }

   public void stop() {

       currentState.stop();

   }

}
(3)具体状态(ConcreteState)
① 停止状态(StoppedState)
// 具体状态:停止状态,实现停止状态下的行为与状态转换

public class StoppedState implements ElevatorState {

   private Elevator elevator; // 持有上下文引用,用于切换状态

   public StoppedState(Elevator elevator) {

       this.elevator = elevator;

   }

   @Override

   public void openDoor() {

       // 停止状态下可开门

       System.out.println("电梯处于停止状态,开门成功~");

   }

   @Override

   public void closeDoor() {

       // 停止状态下可关门(若已开门)

       System.out.println("电梯处于停止状态,关门成功~");

   }

   @Override

   public void runUp() {

       // 停止状态下可切换为上升状态

       System.out.println("电梯从停止状态开始上升~");

       elevator.setState(elevator.RUNNING_UP_STATE); // 切换到上升状态

   }

   @Override

   public void runDown() {

       // 停止状态下可切换为下降状态

       System.out.println("电梯从停止状态开始下降~");

       elevator.setState(elevator.RUNNING_DOWN_STATE); // 切换到下降状态

   }

   @Override

   public void stop() {

       // 已处于停止状态,无需操作

       System.out.println("电梯已处于停止状态,无需重复停止~");

   }

}
② 上升状态(RunningUpState)
// 具体状态:上升状态,实现上升状态下的行为与状态转换

public class RunningUpState implements ElevatorState {

   private Elevator elevator;

   public RunningUpState(Elevator elevator) {

       this.elevator = elevator;

   }

   @Override

   public void openDoor() {

       // 上升状态下不可开门(安全限制)

       System.out.println("电梯正在上升,无法开门!");

   }

   @Override

   public void closeDoor() {

       // 上升状态下门已关闭,无需操作

       System.out.println("电梯正在上升,门已关闭~");

   }

   @Override

   public void runUp() {

       // 已处于上升状态,无需操作

       System.out.println("电梯正在上升,保持运行~");

   }

   @Override

   public void runDown() {

       // 上升状态下不可直接切换为下降状态(需先停止)

       System.out.println("电梯正在上升,无法直接下降!请先停止~");

   }

   @Override

   public void stop() {

       // 上升状态下可切换为停止状态

       System.out.println("电梯停止上升,切换为停止状态~");

       elevator.setState(elevator.STOPPED_STATE); // 切换到停止状态

   }

}
③ 下降状态(RunningDownState)
// 具体状态:下降状态,实现下降状态下的行为与状态转换

public class RunningDownState implements ElevatorState {

   private Elevator elevator;

   public RunningDownState(Elevator elevator) {

       this.elevator = elevator;

   }

   @Override

   public void openDoor() {

       // 下降状态下不可开门(安全限制)

       System.out.println("电梯正在下降,无法开门!");

   }

   @Override

   public void closeDoor() {

       // 下降状态下门已关闭,无需操作

       System.out.println("电梯正在下降,门已关闭~");

   }

   @Override

   public void runUp() {

       // 下降状态下不可直接切换为上升状态(需先停止)

       System.out.println("电梯正在下降,无法直接上升!请先停止~");

   }

   @Override

   public void runDown() {

       // 已处于下降状态,无需操作

       System.out.println("电梯正在下降,保持运行~");

   }

   @Override

   public void stop() {

       // 下降状态下可切换为停止状态

       System.out.println("电梯停止下降,切换为停止状态~");

       elevator.setState(elevator.STOPPED_STATE); // 切换到停止状态

   }

}
(4)客户端测试
public class Client {

   public static void main(String[] args) {

       // 创建上下文对象(电梯)

       Elevator elevator = new Elevator();

       System.out.println("n1. 测试停止状态下的操作:");

       elevator.openDoor(); // 停止状态可开门

       elevator.closeDoor(); // 停止状态可关门

       elevator.runUp(); // 停止状态切换为上升

       System.out.println("n2. 测试上升状态下的操作:");

       elevator.openDoor(); // 上升状态不可开门

       elevator.runUp(); // 上升状态保持运行

       elevator.stop(); // 上升状态切换为停止

       System.out.println("n3. 测试停止状态切换为下降:");

       elevator.runDown(); // 停止状态切换为下降

       System.out.println("n4. 测试下降状态下的操作:");

       elevator.runUp(); // 下降状态不可直接上升

       elevator.stop(); // 下降状态切换为停止

   }

}
输出结果
电梯初始状态:停止

1. 测试停止状态下的操作:

电梯处于停止状态,开门成功~

电梯处于停止状态,关门成功~

电梯从停止状态开始上升~

2. 测试上升状态下的操作:

电梯正在上升,无法开门!

电梯正在上升,保持运行~

电梯停止上升,切换为停止状态~

3. 测试停止状态切换为下降:

电梯从停止状态开始下降~

4. 测试下降状态下的操作:

电梯正在下降,无法直接上升!请先停止~

电梯停止下降,切换为停止状态~

3. 扩展:状态模式的灵活扩展(新增 “故障状态”)

状态模式的核心优势是易于扩展新状态,无需修改原有状态类和上下文类,只需新增具体状态类并实现抽象状态接口:

(1)新增具体状态:故障状态(FaultState)
// 新增具体状态:故障状态

public class FaultState implements ElevatorState {

   private Elevator elevator;

   public FaultState(Elevator elevator) {

       this.elevator = elevator;

   }

   @Override

   public void openDoor() {

       // 故障状态下仅允许手动开门(紧急情况)

       System.out.println("电梯处于故障状态,紧急开门成功(仅手动操作)~");

   }

   @Override

   public void closeDoor() {

       System.out.println("电梯处于故障状态,关门成功~");

   }

   @Override

   public void runUp() {

       System.out.println("电梯处于故障状态,无法上升!");

   }

   @Override

   public void runDown() {

       System.out.println("电梯处于故障状态,无法下降!");

   }

   @Override

   public void stop() {

       // 故障状态下停止操作无效(已停止运行)

       System.out.println("电梯处于故障状态,已停止运行~");

   }

}
(2)上下文扩展(添加故障状态)
public class Elevator {

   // 新增故障状态

   public final ElevatorState FAULT_STATE = new FaultState(this);

   // 其他代码不变...

   // 新增故障触发方法(对外暴露)

   public void triggerFault() {

       System.out.println("n触发电梯故障!");

       this.setState(FAULT_STATE);

   }

}
(3)扩展客户端测试
public class Client {

   public static void main(String[] args) {

       Elevator elevator = new Elevator();

       System.out.println("n5. 测试故障状态:");

       elevator.triggerFault(); // 触发故障

       elevator.openDoor(); // 故障状态紧急开门

       elevator.runUp(); // 故障状态无法上升

       elevator.stop(); // 故障状态已停止

   }

}
扩展输出结果
5. 测试故障状态:

触发电梯故障!

电梯处于故障状态,紧急开门成功(仅手动操作)~

电梯处于故障状态,无法上升!

电梯处于故障状态,已停止运行~

四、状态模式的应用场景

状态模式适用于以下场景:

  1. 对象行为随状态变化而变化:例如,订单状态(待支付→已支付→已发货→已完成),不同状态下的订单支持的操作(取消订单、退款、确认收货)不同。

  2. 避免大量条件判断语句:若上下文类中存在大量 if-elseswitch-case 语句判断状态并执行不同行为,适合用状态模式重构(将条件判断转换为状态类的多态调用)。

  3. 状态转换逻辑复杂:状态之间的转换规则较多,且转换逻辑与状态行为紧密相关(如电梯的状态转换受安全规则限制)。

  4. 需要动态切换状态:对象在运行时需根据外部事件动态切换状态,且切换后行为需自动调整(如播放器的播放 / 暂停 / 停止状态切换)。

五、状态模式的优缺点

优点

  1. 封装状态与行为:将不同状态下的行为封装到独立的状态类中,符合 “单一职责原则”,代码结构清晰。

  2. 消除条件判断:避免上下文类中大量的条件判断语句,通过状态类的多态调用实现行为切换,提高代码可维护性。

  3. 易于扩展新状态:新增状态只需实现抽象状态接口,无需修改原有代码,符合 “开闭原则”(如电梯新增故障状态无需改动原有状态类)。

  4. 状态转换逻辑清晰:状态转换规则集中在具体状态类中,便于理解和维护。

缺点

  1. 类数量增多:每个状态对应一个具体状态类,若状态数量较多(如订单有 10 种状态),会导致系统中类的数量激增,增加开发和维护成本。

  2. 状态类与上下文耦合:具体状态类持有上下文引用(用于切换状态),导致状态类与上下文存在一定耦合(可通过引入状态管理器降低耦合)。

  3. 简单状态场景冗余:若对象只有少数几种状态,且状态转换逻辑简单,使用状态模式会增加不必要的复杂性(直接用条件判断更简洁)。

六、状态模式与命令模式的核心区别

状态模式与命令模式均为行为型设计模式,且都通过封装实现解耦,但核心目标和应用场景差异显著:

维度 状态模式 命令模式
核心目标 封装状态变化与状态对应的行为 封装请求,解耦发送者与接收者
角色关系 上下文持有状态,状态依赖上下文(双向关联) 调用者持有命令,命令持有接收者(单向关联)
行为触发 行为由当前状态决定(状态驱动) 行为由命令对象决定(请求驱动)
状态 / 命令复用 状态可在多个上下文间复用 命令可在多个调用者间复用
典型场景 电梯状态、订单状态、播放器状态 遥控器操作、撤销 / 重做、命令队列

简单来说:状态模式是 “状态决定行为”,命令模式是 “请求触发行为”—— 状态模式聚焦对象自身的状态变化,命令模式聚焦请求的发送与执行分离。

posted @ 2025-12-10 01:44  圣祖帝皇  阅读(4)  评论(0)    收藏  举报