状态(State)模式
本质:根据状态来分离和选择行为。状态模式是状态驱动,由上下文负责
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的逻辑简化。允许一个对象在其内部状态改变时改变它的行为,这个对象看起来似乎修改了它的类。
当某一事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)时,如果在一个类中使用大量的Switch–Case语句是不可取的,维护一大组的Switch–Case语句将是一件异常困难并且容易出错的事情。且这样的话状态逻辑和动作实现没有分离,在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
在State模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的分支语句,并且这些分支依赖于对象的状态时,State模式将每一个分支都封装到独立的类中。
示例性代码
定义一个接口或抽象类,以封装与Context的一个特定状态相关的行为
abstract class State
{
public abstract void Handle(Context context);
}
具体状态: 每一个子类实现一个与Context的一个状态相关的行为
class ConcreteStateA : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
}
}
class ConcreteStateB : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
}
}
class Context
{
private State state;
public Context(State state)
{
this.state = state;
}
public State State
{
get{ return state;}
set
{
state = value;
Console.WriteLine("当前状态:" + state.GetType().Name);
}
}
public void Request()
{
state.Handle(this);
}
}
class Program
{
static void Main(string[] args)
{
Context c = new Context(new ConcreteStateA());
c.Request();
c.Request();
c.Request();
c.Request();
Console.Read();
}
}
例子——工作状态
public class Work
{
private State current;
public Work()
{
current = new ForenoonState();
}
private double hour;
public double Hour
{
get { return hour; }
set { hour = value; }
}
private bool finish = false;
public bool TaskFinished
{
get { return finish; }
set { finish = value; }
}
public void SetState(State s)
{
current = s;
}
public void WriteProgram()
{
current.WriteProgram(this);
}
}
public abstract class State
{
public abstract void WriteProgram(Work w);
}
public class ForenoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 12)
{
Console.WriteLine("当前时间:{0}点上午工作,精神百倍",w.Hour);
}
else
{
w.SetState(new NoonState());
w.WriteProgram();
}
}
}
public class NoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 13)
{
Console.WriteLine("当前时间:{0}点饿了,午饭;犯困,午休。",w.Hour);
}
else
{
w.SetState(new AfternoonState());
w.WriteProgram();
}
}
}
public class AfternoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 17)
{
Console.WriteLine("当前时间:{0}点下午状态还不错,继续努力",w.Hour);
}
else
{
w.SetState(new EveningState());
w.WriteProgram();
}
}
}
public class EveningState : State
{
public override void WriteProgram(Work w)
{
if (w.TaskFinished)
{
w.SetState(new RestState());
w.WriteProgram();
}
else
{
if (w.Hour < 21)
{
Console.WriteLine("当前时间:{0}点加班哦,疲累之极", w.Hour);
}
else
{
w.SetState(new SleepingState());
w.WriteProgram();
}
}
}
}
public class SleepingState : State
{
public override void WriteProgram(Work w)
{
Console.WriteLine("当前时间:{0}点不行了,睡着了。", w.Hour);
}
}
public class RestState : State
{
public override void WriteProgram(Work w)
{
Console.WriteLine("当前时间:{0}点下班回家了", w.Hour);
}
}
class Program
{
static void Main(string[] args)
{
Work emergencyProjects = new Work();
emergencyProjects.Hour = 9;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 10;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 13;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 17;
/*如果要员工必须在20点之前离开公司,只需添加下面的“强制下班
状态”,并改动EveningState类的判断就可以了
emergencyProjects.WorkFinished = true;*/
emergencyProjects.TaskFinished = false;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 19;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 22;
emergencyProjects.WriteProgram();
Console.Read();
}
}
优缺点
优点:
- 将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
- 消除庞大的条件分支语句,把各种状态转移逻辑分布到State的子类之间,减少了相互间的依赖。
- 显式化进行状态转换:为不同的状态引入独立的对象,使得状态的转换变得更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的状况,因为上下文中只有一个变量来记录状态对象,只要为这一个变量赋值就可以了
缺点
- State模式问题主要是逻辑分散化,状态逻辑分布到了很多的State的子类中,很难看到整个的状态逻辑图,这也带来了代码的维护问题
State模式和Strategy模式简单比较
- State模式和Strategy模式有很大程度上的相似:它们都有一个Context类,都是通过委托(组合)给一个具有多个派生类的多态基类实现Context的算法逻辑
- 两者最大的差别就是State模式中派生类持有指向Context对象的引用,并通过这个引用调用Context中的方法,但在Strategy模式中就没有这种情况
实例——工作流中的请假流程
- 某人提出请假申请,先由项目经理审批,如果项目经理不同意,审批就直接结束
- 如项目经理同意,再看是否超过3天,如果三天以内,审批直接结束
- 否则,交给部门经理,部门经理审核后,无论是否同意,审批直接结束
package State;
public abstract class State {
public abstract void approve(Work w);
}
package State;
public class DeptManager extends State{
@Override
public void approve(Work w) {
System.out.println("部门经理同意");
}
}
package State;
public class ProjectManager extends State{
@Override
public void approve(Work w) {
if(w.isAccept()==false){
System.out.println("项目经理不同意");
}else{
if(w.getDay()<=3){
System.out.println("项目经理同意");
}else{
System.out.println("转部门经理处理");
w.setCurrent(new DeptManager());
w.approve();
}
}
}
}
package State;
public class Work {
private State current;
private int day;
private boolean isAccept;
public Work() {
current = new ProjectManager();
}
public State getCurrent() {
return current;
}
public void setCurrent(State current) {
this.current = current;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public boolean isAccept() {
return isAccept;
}
public void setAccept(boolean isAccept) {
this.isAccept = isAccept;
}
public void approve(){
current.approve(this);
}
}
package State;
public class Main {
public static void main(String [] args){
Work w1 = new Work();
w1.setDay(4);
w1.setAccept(true);
w1.approve();
Work w2 = new Work();
w2.setDay(2);
w2.setAccept(false);
w2.approve();
Work w3 = new Work();
w3.setDay(2);
w3.setAccept(true);
w3.approve();
}
}