状态模式
状态模式
本文介绍状态模式定义、结构、特点、适用场景、代码实现。
状态模式
1 模式的定义
状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
2 模式的结构
2.1 结构图
2.2 参与者
- 上下文(Context)保存了对于一个具体状态对象的引用,并会将所有与该状态相关的工作委派给它。上下文通过状态接口与状态对象交互,且会提供一个设置器用于传递新的状态对象。
- 状态(State)接口会声明特定于状态的方法。这些方法应能被其他所有具体状态所理解,因为你不希望某些状态所拥有的方法永远不会被调用。
- 具体状态(Concrete States)会自行实现特定于状态的方法。为了避免多个状态中包含相似代码,你可以提供一个封装有部分通用行为的中间抽象类。状态对象可存储对于上下文对象的反向引用。状态可以通过该引用从上下文处获取所需信息,并且能触发状态转移。
3 模式分析
3.1 优点
- 单一职责原则。将与特定状态相关的代码放在单独的类中。
- 开闭原则。无需修改已有状态类和上下文就能引入新状态。
- 通过消除臃肿的状态机条件语句简化上下文代码。
3.2 缺点
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
4 适用环境
- 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
- 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
5 实现方式
- 确定哪些类是上下文。它可能是包含依赖于状态的代码的已有类;如果特定于状态的代码分散在多个类中,那么它可能是一个新的类。
- 声明状态接口。虽然你可能会需要完全复制上下文中声明的所有方法,但最好是仅把关注点放在那些可能包含特定于状态的行为的方法上。
- 为每个实际状态创建一个继承于状态接口的类。然后检查上下文中的方法并将与特定状态相关的所有代码抽取到新建的类中。在将代码移动到状态类的过程中,你可能会发现它依赖于上下文中的一些私有成员。你可以采用以下几种变通方式:
- 将这些成员变量或方法设为公有。
- 将需要抽取的上下文行为更改为上下文中的公有方法,然后在状态类中调用。这种方式简陋却便捷,你可以稍后再对其进行修补。
- 将状态类嵌套在上下文类中。这种方式需要你所使用的编程语言支持嵌套类。
- 在上下文类中添加一个状态接口类型的引用成员变量,以及一个用于修改该成员变量值的公有设置器。
- 再次检查上下文中的方法,将空的条件语句替换为相应的状态对象方法。
- 为切换上下文状态,你需要创建某个状态类实例并将其传递给上下文。你可以在上下文、各种状态或客户端中完成这项工作。无论在何处完成这项工作,该类都将依赖于其所实例化的具体类。
6 代码实现
https://github.com/august295/DesignPatternCode
参考
[1] https://refactoringguru.cn/design-patterns/state
[2] https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/state.html
本文由作者按照 CC BY 4.0 进行授权