简介
状态模式(State Pattern)也称为状态机模式(State Machine Pattern),是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类,属于行为型模式。
状态模式中类的行为是由状态决定的,不同的状态下有不同的行为。其意图是让一个对象在其内部改变的时候,行为也随之改变。状态模式核心是状态与行为绑定,不同的状态对应不同的行为。
状态模式的应用场景
1、网购订单的状态变化
2、电梯状态的变化
3、工作状态
在软件开发过程中,对于某一项操作,可能存在不同的情况。通常处理多情况问题直接的方式就是使用if...else...或switch...case...条件语句进行枚举。但是这种做法对于复杂状态的判断天然存在弊端:条件判断语句过于臃肿,可读性差,且不具备扩展性,维护难度也大。而如果转换思维,将这些不同状态独立起来用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,消除了if...case...,switch...case...等冗余语句,代码更有层次性且具备良好扩展力。
状态模式主要解决的就是当控制一个对象状态的条件表达式过于复杂时的情况。通过把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。对象的行为依赖于它的状态(属性),并且会根据它的状态改变而改变它的相关行为。状态模式适用于以下场景:
1、行为随状态改变而改变的场景;
2、一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态。
状态模式的通用UML类图:
从UML类图中,我们可以看到,状态模式主要包含三种角色:
1、环境类角色(Context):定义客户端需要的接口,内部维护一个当前状态实例,并负责具体状态的切换;
2、抽象状态角色(State):定义该状态下的行为,可以有一个或多个行为;
3、具体状态角色(ConcreteState):具体实现该状态对应的行为,并且在需要的情况下进行状态切换。
状态模式在源码中的应用
JSF源码中的Lifecycle类
private class PhaseId implements Comparable {
...
private static final PhaseId[] values = {
ANY_PHASE, // 任意一个生命周期阶段
RESTORE_VIEW, // 恢复视图阶段
APPLY_REQUEST_VALUES, // 应用请求值阶段
PROCESS_VALIDATIONS, // 处理输入校验阶段
UPDATE_MODEL_VALUES, // 更新模型的值阶段
INVOKE_APPLICATION, // 调用应用阶段
RENDER_RESPONSE // 显示响应阶段
};
...
}
这些状态的切换都在Lifecycle的execute()方法中进行。其中会传一个参数FacesContext
对象,最终所有的状态都被FacesContext
保存。
状态模式相关的设计模式
1、状态模式于责任链模式
状态模式和责任链模式都能消除if分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么这种情况下。两种模式都可以使用。
从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变。
从代码实现上来看,他们间最大的区别就是状态模式各个状态对象知道自己下一个要进入的状态对象;而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。
2、状态模式与策略模式
状态模式与策略模式的UML类图架构几乎完全一样,但是他们的应用场景是不一样的。策略模式多种算法行为择其一都能满足,彼此之间是独立地,用户可自行更换策略算法;
而状态模式各个状态间是存在相互关系的,彼此之间在一定条件下存在自动切换状态效果,且用户无法指定状态,只能设置初始状态。
状态模式的优缺点
优点:
1、结构清晰:将状态独立为类,消除了冗余的if...else或switch...case语句,使代码更加简洁,提高系统可维护性;
2、将状态转换显示化:通常的对象内部都是使用数值类型来定义状态,状态的切换是通过赋值进行表现,不够直观;而使用状态类,在切换状态时,是以不同的类进行表示,转化目的更加明确;
3、状态类职责明确且具备扩展性
缺点:
1、类膨胀:如果一个事务具备很多状态,则会造成状态类太多;
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;
3、状态模式对开闭原则的支持并不友好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某些状态类的行为也需要修改对应类的源代码。