一、简介(Brief Introduction)
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,运行。每一种状态改变,都有可能要根据其他状态来更新处理。例如,开门状体,你不能在运行的时候开门,而是在电梯定下后才能开门。
例子2:我们给一部手机打电话,就可能出现这几种情况:用户开机,用户关机,用户欠费停机,用户消户等。 所以当我们拨打这个号码的时候:系统就要判断,该用户是否在开机且不忙状态,又或者是关机,欠费等状态。但不管是那种状态我们都应给出对应的处理操作。
二、模式分析(Analysis)
环境类(Context): 定义客户感兴趣的接口。维护一个ConcreteState子类的实例,将与状态相关的操作委托给当前的Concrete State对象来处理,这个实例定义当前状态。
抽象状态类(State): 定义一个接口以封装与Context的一个特定状态相关的行为。
具体状态类(ConcreteState): 每一子类实现一个与Context的一个状态相关的行为。
三、案例分析(Example)
namespace _16._7工作状态_状态模式版
{
1、客户端
class Program
{
static void Main(string[] args)
{
//紧急项目
Work emergencyProjects = new Work();
emergencyProjects.Hour = 9;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 10;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 13;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 17;
//emergencyProjects.TaskFinished = true;
emergencyProjects.TaskFinished = false;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 19;
emergencyProjects.WriteProram();
emergencyProjects.Hour = 22;
emergencyProjects.WriteProram();
Console.Read();
}
}
2、抽象状态
public abstract class State
{
public abstract void WriteProgram(Work w);
}
3、上午和中午工作状态
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.WriteProram ();
}
}
}
4、中午工作状态
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.WriteProram();
}
}
}
5、下午和傍晚工作状态
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.WriteProram();
}
}
}
7、晚间工作状态
public class EveningState : State
{
public override void WriteProgram(Work w)
{
if (w.TaskFinished)
{
w.SetState(new RestState());
w.WriteProram();
}
else
{
if (w.Hour < 21)
{
Console.WriteLine("当前时间: {0}点 加班哦,疲惫至极", w.Hour);
}
else
{
w.SetState(new SleepingState());
w.WriteProram();
}
}
}
}
8、睡眠状态和下班休息状态类
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);
}
}
9、工作类
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 WriteProram()
{
current.WriteProgram(this);
}
}
}
四、解决的问题(What To Solve)
主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。
在下面的两种情况下均可使用State模式:
• 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
• 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。
五、优缺点(Advantage and Disadvantage)
优点:
(1)所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。
(2)状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。
缺点:
(1)状态模式的使用必然会增加系统类和对象的个数。
六、扩展(Extend)
在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。
七、联系(Link)
(1)职责链模式:
职责链模式和状态模式都可以解决If分支语句过多,
从定义来看:
- 状态模式是一个对象的内在状态发生改变(一个对象,相对比较稳定,处理完一个对象下一个对象的处理一般都已确定)
- 职责链模式是多个对象之间的改变(多个对象之间的话,就会出现某个对象不存在的现在,就像我们举例的公司请假流程,经理可能不在公司情况),这也说明他们两个模式处理的情况不同。
可以理解为
状态模式:
相当于If else if else;
设计路线:各个State类的内部实现(相当于If,else If内的条件)
执行时通过State调用Context方法来执行。
职责链模式:
相当于Swich case
设计路线:客户设定,每个子类(case)的参数是下一个子类(case)。
使用时,向链的第一个子类的执行方法传递参数就可以。
区别就是状态模式是让各个状态对象自己知道其下一个处理的对象是谁,而职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定。
(2)策略模式(状态模式与策略模式是孪生兄弟)
状态模式和策略模式的实现方法非常类似,都是利用多态把一些操作分配到一组相关的简单的类中,因此很多人认为这两种模式实际上是相同的。
然而在现实世界中,策略(如促销一种商品的策略)和状态(如同一个按钮来控制一个电梯的状态,又如手机界面中一个按钮来控制手机)是两种完全不同的思想。当我们对状态和策略进行建模时,这种差异会导致完全不同的问题。例如,对状态进行建模时,状态迁移是一个核心内容;然而,在选择策略时,迁移与此毫无关 系。另外,策略模式允许一个客户选择或提供一种策略,而这种思想在状态模式中完全没有。
一个策略是一个计划或方案,通过执行这个计划或方案,我们可以在给定的输入条件下达到一个特定的目标。策略是一组方案,他们可以相互替换;选择一个策 略,获得策略的输出。策略模式用于随不同外部环境采取不同行为的场合。而状态模式不同,对一个状态特别重要的对象,通过状态机来建模一个对象的状态;状态模式处理的核心 问题是状态的迁移,因为在对象存在很多状态情况下,对各个business flow,各个状态之间跳转和迁移过程都是及其复杂的。
例如一个工作流,审批一个文件,存在新建、提交、已修改、HR部门审批中、老板审批中、HR审批失败、老板审批失败等状态,涉及多个角色交互,涉及很多 事件,这种情况下用状态模式(状态机)来建模更加合适;把各个状态和相应的实现步骤封装成一组简单的继承自一个接口或抽象类的类,通过另外的一个 Context来操作他们之间的自动状态变换,通过event来自动实现各个状态之间的跳转。在整个生命周期中存在一个状态的迁移曲线,这个迁移曲线对客 户是透明的。我们可以参考微软最新的WWF 状态机工作流实现思想。
在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;
而策略模式里,采取何种策略由外部条件(C)决定。
他们应用场景(目的)却不一样,State模式重在强调对象内部状态的变化改变对象的行为,Strategy模式重在外部对策略的选择,策略的选择由外部条件决定,也就是说算法的动态的切换。但由于它们的结构是如此的相似,我们可以认为“状态模式是完全封装且自修改的策略模式”。即状态模式是封装对象内部的状态的,而策略模式是封装算法族的
八、总结(Summary)
状 态模式的主要优点在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即 可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;其缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结 构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。