状态模式(State Pattern) – 设计模式之行为模式:
目录
Context定义客户端需要的接口,并且负责具体状态的切换。
状态模式(State Pattern)
定义: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
好处: 将特定状态相关的行为局部化,并且将不同状态的行为分割开来。
封装基于状态的行为,并将行为委托到当前状态。
类图
状态模式通用类图:
例子-审批:
过程:
在工作中,员工出差等需要进行报销审批,审批需要通过主管,经理和总监进行审批。(PS:这里模拟审批状态的一个过程,也没有想到好的状态的一个例子,刚好责任链模式也用这个例子,可以做一个对比。)
类图:
代码:
状态 AbstractState
接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换
public abstract class AbstractState {
protected Context context;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public abstract void handle();
}
Context定义客户端需要的接口,并且负责具体状态的切换。
public class Context {
private AbstractState AbstractState;
public Context() {
}
public AbstractState getAbstractState() {
return AbstractState;
}
public void setAbstractState(AbstractState abstractState) {
this.AbstractState = abstractState;
this.AbstractState.setContext(this);
}
public void request(){
this.AbstractState.handle();
}
}
每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。
主管 SupervisorState
public class SupervisorState extends AbstractState {
@Override
public void handle() {
System.out.println("主管审批通过,下一个经理审批");
context.setAbstractState(new ManagerState());
}
}
经理 ManagerState
public class ManagerState extends AbstractState {
@Override
public void handle() {
System.out.println("经理审批通过,下一个总监审批");
context.setAbstractState(new DirectorState());
}
}
总监 DirectorState
public class DirectorState extends AbstractState {
@Override
public void handle() {
System.out.println("总监审批通过,告知财务打款");
//审核通过之后的逻辑
System.out.println("财务打款5500元");
}
}
测试:
public class ApproveTest {
public static void main(String[] args) {
Context context = new Context();
context.setAbstractState(new SupervisorState());
context.request();
context.request();
context.request();
}
}
结果:
主管审批通过,下一个经理审批
经理审批通过,下一个总监审批
总监审批通过,告知财务打款
财务打款5500元
分析
这个例子中会有一个大的问题,主管类里面依赖了经理的类,增加了耦合度。考虑把下一个状态的过渡,移到context里面进行处理。
优化-审批:
类图:
代码:
状态 State
public interface State {
void changeApprove(ApproveContext context);
}
ApproveContext
public class ApproveContext {
private State state;
// 默认一个状态初始值,常态
public ApproveContext() {
state = new SupervisorState();
this.state.changeApprove(this);
}
public void approveNext() {
if (state.getClass().equals(SupervisorState.class)) {
changeStateToNext(new ManagerState());
} else if (state.getClass().equals(ManagerState.class)){
changeStateToNext(new DirectorState());
}
}
private void changeStateToNext(State newState) {
this.state = newState;
this.state.changeApprove(this);
}
@Override
public String toString() {
return "申请报销 ";
}
}
把状态如何过渡到其他状态的控制移到context里面进行处理
主管 SupervisorState
public class SupervisorState implements State {
@Override
public void changeApprove(ApproveContext context) {
System.out.println(context+"主管审批通过,下一个经理审批");
context.approveNext();
}
}
经理 ManagerState
public class ManagerState implements State {
@Override
public void changeApprove(ApproveContext context) {
System.out.println(context+"经理审批通过,下一个总监审批");
context.approveNext();
}
}
总监 DirectorState
public class DirectorState implements State {
@Override
public void changeApprove(ApproveContext context) {
System.out.println(context+"总监审批通过,告知财务打款");
//审核通过之后的逻辑
System.out.println("财务打款5500元");
}
}
测试:
public class ApproveTest {
public static void main(String[] args) {
ApproveContext context = new ApproveContext();
context.approveNext();
}
}
结果:
申请报销 主管审批通过,下一个经理审批
申请报销 经理审批通过,下一个总监审批
申请报销 总监审批通过,告知财务打款
财务打款5500元
总结:
在实际中也很少用到状态模式,更多是学习下状态模式的思维。
优点:
1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:
1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景
1, 行为随状态改变而改变的场景
这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结
果也会不同,在这种情况下需要考虑使用状态模式。
2, 条件、分支判断语句的替代者
在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。
对比策略模式
我觉得两者比较相近,最大的区别是状态模式中具体实现类中:本状态如何过渡到其他状态有一个过渡到其它状态的处理。除去这个,就变成策略模式了。