状态模式(State Pattern)
一、概念
状态模式(State Pattern)也称为状态机模式(State Machine Pattern),当控制一个对象状态的条件表达式过于复杂的时候,就可以考虑使用状态模式,通过把状态的判断逻辑转移到表示不同状态的一系列类中,这样就可以把复杂的逻辑简单化,使得对象的行为依赖于它的状态,并且会随着状态的改变而同时改变行为。
-
所谓对象的状态,通常指的就是对象实例的
属性的值
;而行为指的就是对象的功能
,再具体点说,行为大多可以对应到方法上
。
-
状态模式的核心就是
分离对象的状态和行为
,通过维护状态的变化,来调用不同状态对应的不同功能。 也就是说,状态和行为是相关联的,即: 状态决定行为(功能)
-
由于状态是在
运行期被改变的
,因此行为也会在运行期根据状态的改变而改变
。 - 和 策略模式 一样可以减少if-else或switch-case等条件判断逻辑。
二、适用场景
状态模式主要应用于以下场景:
- 对象的
行为(功能)
需要随着状态的改变而改变时。 - 当我们一个操作中需要根据状态来写大量的if/else逻辑时
三、参与者
- 环境类角色/状态处理角色(Context):定义客户类需要的接口,内部一个
具体状态的实例
,并负责具体状态的切换
- 在状态模式中,
环境(Context)是持有状态的对象
,但是环境(Context)自身并不处理跟状态相关的行为,而是把处理状态的功能委托给了状态对应的状态处理类来处理。
- 在状态模式中,
- 抽象状态角色(Abstract State):接口或者抽象类。定义每个状态下对应的
行为(功能方法)
,可以有一个或者多个行为(功能方法)
。 - 具体状态角色/上下文角色(Concrete State):具体实现该状态对应的
行为(功能方法)
,并且在需要的情况下实现状态的切换。- 具体的状态处理类中经常需要
获取环境(Context)自身的数据
,甚至在必要的时候会回调环境(Context)的方法,因此,通常将环境(Context)自身当作一个参数传递给具体的状态处理类
- 具体的状态处理类中经常需要
- 客户类(Client):使用环境类角色完成状态装换
- 客户端一般只和环境(Context)交互。客户端可以用状态对象来配置一个环境(Context),一旦配置完毕,就不再需要和状态对象打交道了。客户端通常不负责运行期间状态的维护,也不负责决定后续到底使用哪一个具体的状态处理对象。
四、代码例子
绘图板它有三种画笔颜色,分别是红色、黄色、蓝色,我们可以设置当前画笔的颜色之后再绘图板上绘制指定图形
环境类角色/状态处理角色(Context):WhiteBoard
抽象状态角色(Abstract State):DrawState
具体状态角色/上下文角色(Concrete State):DrawRed、DrawYellow、DrawBlue
五、UML图
六、优缺点
(一)优点
- 通过
将每个状态设置为独立的对象
,消除了代码中存在的大量if/else等判断分支
,使得代码更加简洁,更容易维护。 将不同的状态通过不同的类来表示
,使得状态切换
时比用数字或者字符串来表示时更加直观
转换目的也更加明确。- 每个状态类的职责单一明确,易于扩展。
- 封装性好,
状态的转换在类的内部实现
,外部调用就不需要知道类的内部是如何实现状态和行为的变换了。
(二)缺点
- 状态过多会引起
类膨胀(事实上这也是大部分设计模式的通病)
。 - 对于支持状态切换的
环境类(Context)违反了开闭原则
,因为一旦状态修改或者中间要新增状态,则需要修改对应的源代码
,否则会出现状态切换错误。 - 状态模式的结构与实现相对较为复杂,容易造成代码混乱。
七、与其他模式的区别
(一)状态模式与责任链模式
上面的状态模式的实现是有点和责任链模式相似,都是一条链去处理,可以这么说在某种场景下这两种模式可以互相替换,但是这两种模式也是有本质区别的。
-
状态模式的
下一个节点
是各个状态对象已经了解的,而且状态的流转就是由内部进行流转
,客户端无法决定。
-
责任链模式的
“链路”上的对象并不知道下一个节点处理人是谁
,而是由客户端自行组装决定的。
(二)状态模式与策略模式
状态模式和策略模式都能用来消除大量的if/else场景,但是也有本质区别。
1.思想不同
- 状态模式是: 将类的"状态"封装了起来,在运行时进行自动的转换,从而实现,类在同一行为下不同状态得都不同的个。它与策略模式的区别在于,这种转换是"自动","无意识"的。
- 策略模式是:将类的变化的部分抽离出来,组合进类中,根据不同的子类替换实现动态改变
行为(功能)
。
2.角色的不同
策略模式
- Strategy: 定义所有支持的算法的公共接口抽象类.
- ConcreteStrategy: 封装了具体的算法或行为,继承于Strategy
- Context: 用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
状态模式
- State: 抽象状态类,定义一个接口以封装与context的一个状态相关的行为
- ConcreteState: 具体状态,每一子类实现一个与Context的一个状态相关的行为
- Context: 维护一个ConcreteState子类的实例,这个实例定义当前的状态。
3.环境角色的职责不同
- 两者都有一个叫做Context环境角色的类,但区别很大,策略模式的环境角色只是一个
委托作用,负责策略的替换
;而状态模式的环境角色不仅仅是委托行为,它还具有记录状态变化
的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。
4.解决问题的重点不同
- 策略模式旨在解决
内部策略如何改变的问题
,也就是将内部策略的改变对外界的影响降低到最小,使策略可以自由地切换
;而状态模式旨在解决内在状态的改变而引起行为(功能)
改变的问题,它的出发点是状态
,封装状态而暴露行为(功能)
,一个对象的状态改变,从外界来看就好像是行为(功能)
改变。