0%

状态模式

状态模式(State Pattern):基于状态的行为动态切换

状态模式是行为型设计模式的一种,核心思想是允许对象在内部状态改变时自动改变其行为,使对象看起来好像修改了它的类。这种模式将不同状态对应的行为封装到独立的状态类中,通过状态的切换实现行为的动态变化,本质是 “将状态转换逻辑与状态对应的行为分离,用类表示状态”。就像 “交通信号灯”—— 红灯、黄灯、绿灯对应不同的行为(禁止通行、准备停止、允许通行),灯的状态改变时,行为也随之改变。

状态模式的核心结构

状态模式通过三个核心角色实现状态与行为的绑定及动态切换:

上下文(Context)

  • 维护当前状态并提供切换状态的接口,是状态模式的使用者。
  • 上下文本身不实现具体行为,而是将行为委托给当前状态对象。
  • 示例:TrafficLight(交通信号灯,持有当前灯状态的引用)。

状态接口(State)

  • 定义所有具体状态的公共接口,声明状态对应的行为方法(如handle())。
  • 示例:LightState(灯状态接口,声明show()方法表示灯的显示行为)。

具体状态(ConcreteState)

  • 实现状态接口,封装特定状态下的行为逻辑,并可在适当时候触发状态切换(通过修改上下文的当前状态)。
  • 示例:RedLight(红灯状态)、YellowLight(黄灯状态)、GreenLight(绿灯状态)。

代码实现示例

以 “交通信号灯” 为例,展示状态模式的实现:信号灯有红、黄、绿三种状态,每种状态对应不同的显示行为,且状态会按 “红→绿→黄→红” 的顺序切换。

1. 状态接口与具体状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 1. 状态接口:灯状态
public interface LightState {
void show(TrafficLight light); // 显示当前状态的行为,参数为上下文
}

// 2. 具体状态1:红灯(禁止通行)
public class RedLight implements LightState {
@Override
public void show(TrafficLight light) {
System.out.println("红灯亮:禁止通行");
// 3秒后切换到绿灯
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
light.setState(new GreenLight()); // 切换状态
}
}

// 3. 具体状态2:绿灯(允许通行)
public class GreenLight implements LightState {
@Override
public void show(TrafficLight light) {
System.out.println("绿灯亮:允许通行");
// 5秒后切换到黄灯
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
light.setState(new YellowLight()); // 切换状态
}
}

// 4. 具体状态3:黄灯(准备停止)
public class YellowLight implements LightState {
@Override
public void show(TrafficLight light) {
System.out.println("黄灯亮:准备停止");
// 2秒后切换到红灯
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
light.setState(new RedLight()); // 切换状态
}
}

2. 上下文(交通信号灯)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 上下文:交通信号灯
public class TrafficLight {
private LightState currentState; // 当前状态

// 初始化状态为红灯
public TrafficLight() {
this.currentState = new RedLight();
}

// 设置当前状态(切换状态)
public void setState(LightState state) {
this.currentState = state;
}

// 触发当前状态的行为
public void run() {
while (true) { // 循环切换状态
currentState.show(this); // 委托给当前状态处理
}
}

public static void main(String[] args) {
TrafficLight light = new TrafficLight();
light.run(); // 启动信号灯
}
}
输出结果(循环执行)
1
2
3
4
5
红灯亮:禁止通行
绿灯亮:允许通行
黄灯亮:准备停止
红灯亮:禁止通行
...(循环)

状态模式的核心优势

  1. 行为与状态的清晰绑定
    每个状态对应的行为被封装在独立的状态类中,取代了传统的if-elseswitch判断,代码结构更清晰,可读性更强。
  2. 动态切换行为
    上下文通过切换状态对象实现行为的动态改变,无需修改上下文或状态类的结构,符合开闭原则(新增状态只需添加新的具体状态类)。
  3. 状态转换逻辑集中
    状态转换的逻辑可在具体状态类中实现(如红灯自动切换到绿灯),避免转换逻辑分散在上下文的多个方法中。
  4. 单一职责原则
    每个状态类仅负责自身状态下的行为,职责单一,便于维护和扩展。

适用场景

  1. 对象行为依赖于状态且状态多变
    当一个对象的行为随其状态变化而变化,且状态数量较多(如:
    • 订单状态:待支付→已支付→待发货→已发货→已完成(每个状态对应不同操作)。
    • 电梯状态:上升→下降→停止(每个状态下按钮的响应不同)。
    • 播放器状态:播放→暂停→停止(每个状态下的操作行为不同)。
  2. 避免复杂的条件判断
    若代码中存在大量与状态相关的if-elseswitch语句(如if (state == "RED") { ... } else if (state == "GREEN") { ... }),状态模式可将这些判断转换为状态类的多态调用,简化代码。
  3. 状态转换规则复杂
    当状态转换需要依赖历史状态或外部条件时,将转换逻辑封装在状态类中可使逻辑更清晰(如游戏角色的状态切换:正常→受伤→死亡,转换条件与血量相关)。

优缺点分析

优点

  • 代码清晰:用类表示状态,取代条件判断,提高可读性和可维护性。
  • 扩展性好:新增状态只需添加具体状态类,无需修改现有代码。
  • 状态转换灵活:状态转换逻辑可在状态类中灵活实现,支持复杂的转换规则。

缺点

  • 类数量增加:每个状态对应一个类,状态数量过多时会导致类数量膨胀(但相比混乱的条件判断,通常利大于弊)。
  • 状态类依赖上下文:具体状态类需要持有上下文引用以实现状态切换,可能增加耦合(但通过接口交互可降低依赖)。

经典应用案例

  1. 订单状态管理
    电商系统中,订单的状态(待支付、已支付、待发货等)对应不同的操作(支付、取消、发货等),通过状态模式可清晰管理每个状态的行为及转换逻辑。
  2. 电梯控制
    电梯的状态(上升、下降、停止)决定了按钮的响应行为(如上升状态下不响应下方楼层按钮),状态模式可简化电梯控制逻辑。
  3. 有限状态机(FSM)
    状态模式是实现有限状态机的核心模式,广泛应用于游戏 AI、工作流引擎等领域(如游戏中 NPC 的状态切换:巡逻→攻击→逃跑)。
  4. UI 组件状态
    如按钮的状态(可用、禁用、选中)对应不同的显示和交互行为,通过状态模式可分离每种状态的逻辑。

总结

状态模式通过将状态抽象为类,实现了对象行为与状态的动态绑定,有效解决了 “行为依赖状态且状态多变” 的问题。其核心价值在于用多态替代条件判断,使代码更清晰、更易于扩展。在对象状态较多且行为随状态变化的场景中,状态模式是优于复杂条件判断的理想选择

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10