状态模式(State Pattern)详解
文章目录
- 一、状态模式简介
- 1.1 什么是状态模式?
- 1.2 为什么需要状态模式?
- 1.3 状态模式的核心思想
- 二、状态模式的结构
- 2.1 UML类图
- 2.2 各个组件的详细说明
- 2.3 交互过程
- 三、状态模式的实现步骤(以Java为例)
- 步骤1:创建状态接口
- 步骤2:实现具体状态类
- 步骤3:创建上下文类
- 步骤4:客户端代码
- 四、状态模式的各种实现方式
- 4.1 状态转换的控制权
- 1. 状态类控制转换(自治型)
- 2. 上下文类控制转换(外部驱动型)
- 4.2 状态对象的创建时机
- 1. 预先创建所有状态对象(单例模式)
- 2. 按需创建状态对象
- 4.3 状态转换表驱动的实现
- 五、状态模式的多个Java示例
- 5.1 简单示例:电灯开关
- 5.2 中等复杂度示例:多模式电灯
- 5.3 高级示例:音乐播放器
- 六、状态模式的进阶内容
- 6.1 如何处理状态转换的历史记录
- 6.2 状态模式与单例模式结合
- 6.3 使用枚举实现状态模式
- 6.4 使用函数式接口实现状态模式
- 七、状态模式的常见问题与最佳实践
- 7.1 状态爆炸问题
- 7.2 状态模式与策略模式的区别
- 7.3 状态模式与观察者模式的区别
- 7.4 状态模式与责任链模式的区别
- 7.5 状态模式与备忘录模式的区别
- 7.6 状态模式与单例模式的区别
- 7.7 状态跟踪和调试
- 7.8 状态模式与观察者模式结合
- 7.9 防止无限状态转换
- 八、状态模式的适用场景与不适用场景
- 8.1 适用场景
- 8.2 不适用场景
- 九、状态模式与其他相关模式的对比
- 9.1 状态模式 vs 策略模式
- 9.2 状态模式 vs 命令模式
- 9.3 状态模式 vs 备忘录模式
- 十、常见问题解答
- 10.1 初学者常见问题
- 10.2 进阶问题
- 十一、真实世界中的状态模式应用
- 11.1 Java中的状态模式实例
- 11.2 Android中的Fragment生命周期
- 11.3 游戏开发中的角色状态
- 十二、总结与最佳实践
- 12.1 状态模式的核心优势
- 12.2 实现状态模式的最佳实践
- 12.3 最终思考
一、状态模式简介
1.1 什么是状态模式?
状态模式是一种行为设计模式,它允许对象在内部状态改变时改变它的行为。这种模式将状态相关的行为抽取到独立的状态类中,使得代码更加清晰、可维护,并且可以在运行时自由切换对象的状态。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的上下文对象。
1.2 为什么需要状态模式?
在很多场景中,一个对象的行为取决于它的状态,而且它必须在运行时根据状态改变它的行为。考虑一个简单的例子:一个电灯开关。按一下开关,灯亮;再按一下,灯灭。在这个例子中,开关的行为(按下后发生什么)完全取决于它当前的状态(开或关)。
如果不使用状态模式,我们可能会这样实现:
public class Light {private boolean isOn = false;public void press() {if (isOn) {System.out.println("关灯");isOn = false;} else {System.out.println("开灯");isOn = true;}}
}
这种方法对于简单的状态转换是可行的,但随着状态数量的增加和状态转换逻辑的复杂化,代码会变得难以维护:
public class ComplexLight {private enum State { OFF, LOW, MEDIUM, HIGH }private State state = State.OFF;public void press() {switch (state) {case OFF:System.out.println("低亮度模式");state = State.LOW;break;case LOW:System.out.println("中亮度模式");state = State.MEDIUM;break;case MEDIUM:System.out.println("高亮度模式");state = State.HIGH;break;case HIGH:System.out.println("关灯");state = State.OFF;break;}}
}
当状态和行为更加复杂时,这种方法会导致大量的条件语句和难以维护的代码。
1.3 状态模式的核心思想
状态模式的核心思想是:
- 将每个状态封装成独立的类,每个类实现一个共同的接口
- 将与状态相关的行为委托给当前状态对象
- 允许状态对象在需要时改变上下文的状态
通过这种方式,我们可以:
- 避免复杂的条件语句
- 使状态转换更加明确
- 容易添加新的状态
- 提高代码的可维护性
二、状态模式的结构
2.1 UML类图
状态模式的UML类图如下:
┌───────────┐ ┌───────────┐
│ Context │◆──────────│ State │
└───────────┘ └───────────┘
│ state │ △
└───────────┘ │
│ request() │ │
└───────────┘ │┌───────┴───────┐│ │┌──────────────┐ ┌──────────────┐│ ConcreteStateA│ │ConcreteStateB│└──────────────┘ └──────────────┘│ handle() │ │ handle() │└──────────────┘ └──────────────┘
2.2 各个组件的详细说明
-
State(状态)接口:
- 定义所有具体状态类的共同接口
- 封装与Context的一个特定状态相关的行为
-
ConcreteState(具体状态)类:
- 每个类实现一个与Context的某个状态相关的行为
- 实现State接口定义的方法
- 可以访问Context对象,以便在必要时进行状态转换
-
Context(上下文)类:
- 维护一个ConcreteState子类的实例,这个实例定义了当前状态
- 将与状态相关的请求委托给当前的状态对象
- 提供一个接口让状态对象可以访问Context
2.3 交互过程
- Context将与状态相关的请求委托给当前的状态对象
- Context可以将自身传递给状态对象,使得状态对象可以访问Context
- Context是客户端使用的主要接口;客户端通常不直接与状态对象交互
- 客户端或者Context可以触发状态转换
三、状态模式的实现步骤(以Java为例)
让我们通过一个简单的灯光控制示例,一步步实现状态模式:
步骤1:创建状态接口
首先,我们需要定义一个表示状态的接口:
// 状态接口
public interface LightState {void handlePress(LightSwitch lightSwitch);String getState();
}
步骤2:实现具体状态类
然后,为每个状态创建一个实现该接口的类:
// 关闭状态
public class OffState implements LightState {@Overridepublic void handlePress(LightSwitch lightSwitch) {System.out.println("打开灯");lightSwitch.setState(new OnState());}@Overridepublic String getState() {return "关闭状态";}
}// 打开状态
public class OnState implements LightState {@Overridepublic void handlePress(LightSwitch lightSwitch) {System.out.println("关闭灯");lightSwitch.setState(new OffState());}@Overridepublic String getState() {return "打开状态";}
}
步骤3:创建上下文类
接下来,创建一个上下文类,它将使用状态类:
// 上下文:灯光开关
public class LightSwitch {private LightState currentState;// 初始状态是关闭的public LightSwitch() {this.currentState = new OffState();}// 设置当前状态public void setState(LightState state) {this.currentState = state;}// 按下开关public void press() {System.out.println("当前状态:" + currentState.getState());currentState.handlePress(this);}
}
步骤4:客户端代码
最后,客户端可以使用上下文对象而不需要直接处理状态:
public class StatePatternDemo {public static void main(String[] args) {LightSwitch lightSwitch = new LightSwitch();// 第一次按下开关:从关闭到打开lightSwitch.press();// 第二次按下开关:从打开到关闭lightSwitch.press();// 第三次按下开关:从关闭到打开lightSwitch.press();}
}
输出结果:
当前状态:关闭状态
打开灯
当前状态:打开状态
关闭灯
当前状态:关闭状态
打开灯
四、状态模式的各种实现方式
状态模式有多种实现方式,下面我们来看看几种常见的变体:
4.1 状态转换的控制权
状态转换的控制权可以放在不同的地方:
1. 状态类控制转换(自治型)
在这种方式中,状态类自己决定何时以及如何转换到其他状态:
public class SelfDrivenState implements State {@Overridepublic void handle(Context context) {// 处理请求doSomething();// 自己决定状态转换context.setState(new NextState());}
}
2. 上下文类控制转换(外部驱动型)
在这种方式中,上下文类决定状态转换:
public class Context {private State state;public void request() {state.handle(this);// 上下文决定状态转换if (someCondition()) {setState(new StateA());} else {setState(new StateB());}}
}
4.2 状态对象的创建时机
状态对象可以在不同时间创建:
1. 预先创建所有状态对象(单例模式)
public class Context {// 预先创建所有状态对象private static final State STATE_A = new ConcreteStateA();private static final State STATE_B = new ConcreteStateB();private State currentState = STATE_A;public void setState(State state) {this.currentState = state;}public void requestA() {// 使用预先创建的状态对象setState(STATE_B);}
}
2. 按需创建状态对象
public class Context {private State state;public void setState(State state) {this.state = state;}public void request() {// 需要时创建新的状态对象setState(new ConcreteStateB());}
}
4.3 状态转换表驱动的实现
对于复杂的状态机,可以使用状态转换表来定义状态转换规则:
public class StateMachine {// 状态转换表:当前状态 -> 事件 -> 下一个状态private Map<State, Map<Event, State>> transitionTable = new HashMap<>();private State currentState;public StateMachine() {// 初始化状态转换表Map