说到状态模式,书上给出的解释是:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。看不懂?没关系,毕竟我们都不是小菜。
我们还是从熟悉的状态图说起吧。
故事是这样的:我们来看一个人他的下午时光是如何度过的。在12:00~13:00之间,他在吃饭。过了13点,他就开始午睡。睡醒以后,到了15点,他就出去玩了。也就是说,他的下午时间就在吃饭——睡觉——玩耍之间转换。
用状态图描述,基本上是这个样子的。(简化理解)
联系我们的状态模式,其实状态模式根本上就是几个状态之间的转换,但精髓之处在于如何使得我们的代码更好,设计更完善。这就要结合我们之前讲的设计模式的一些原则和面向对象设计的知识了。首先,方法过长是坏味道。一般来看,状态之间的转换我们会用到很多的分支语句,这样使得一个方法代码过长,那么它的责任就过大了。所以,我们要让代码的责任分解。分解之后,需求改变之后代码的改动就会减少很多了。这也就符合之前说的开放-封闭原则。
用代码来表示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//紧急项目
lifeState emergencyProjects = new lifeState();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 18;
emergencyProjects.WriteProgram();
emergencyProjects.TaskFinished = false;
Console.Read();
}
}
//抽象状态
public abstract class State
{
public abstract void WriteProgram(lifeState w);
}
//吃饭状态
public class EatingState : State
{
public override void WriteProgram(lifeState w)
{
if (w.Hour <13)
{
Console.WriteLine( "我在吃饭",w.Hour);
}
else
{
w.SetState(new SleepingState()); w.WriteProgram(); //12点之后,就开始午睡了,进入睡觉状态
}
}
}
//睡觉状态
public class SleepingState : State
{
public override void WriteProgram(lifeState w)
{
if (w.Hour <15)
{
Console.WriteLine("我在睡觉",w.Hour);
}
else
{
w.SetState(new PlayingState()); w.WriteProgram(); //14点之后,进入玩耍状态
}
}
}
//玩耍状态
public class PlayingState : State
{
public override void WriteProgram(lifeState w)
{
Console.WriteLine("我在玩",w.Hour);
}
}
public class lifeState
{
private State current;
public lifeState()
{
current = new EatingState();
}
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);
}
}
}
运行结果:
看完这个小例子,我们来看一下状态模式的结构。我们把特定的状态相关的行为(如吃饭)放在一个对象中,因为代码都在Eating中,所以通过定义新的子类就可以很容易的增加新的状态和转换。把各个状态转移逻辑分布到Sleeping,Playing,Eating之中,减少了相互之间的依赖。