问题:
在面向对象软件设计时,常常碰到某一个对象由于状态的不同而有不同的行为。我们可以采用switch…case语句来解决问题,但是,如果这种状态变化比较频繁(状态的数量会变化),状态、判断逻辑、行为耦合在一起,当系统中需要加入新的状态时,必须修改判断逻辑(加入新的case...)。
定义:
状态模式(State Pattern)是一种行为模式。当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
意图:
定义一个Context,接收客户端配置的具体状态对象,并将与状态相关的请求委托给当前的ConcreteState对象处理,并将Context自身作为一个参数传递给处理该请求的状态对象,这使得状态对象在执行完成后,可以访问context修改状态(也可以由context控制修改)。State模式把状态的判断逻辑转移到表示不同状态的一系列类中,所有与一个特定状态相关的行为都放入每个单独的类中,使得复杂的判断逻辑简单化,并达到了解耦的目的。
参与者:
•环境(Context)角色:维护一个ConcreteState子类的一个实例,这个实例定义当前的状态。
•状态(State)角色:抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
•具体状态(ConcreteState)角色:每一个子类实现一个与Context的一个状态相关的行为。
UML:
代码说明:
/// Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
/// </summary>
public class Context
{
/// <summary>
/// 存储状态
/// </summary>
public StateClass State { get; set; }
/// <summary>
/// 定义Context的初始状态
/// </summary>
/// <param name="state"></param>
public Context(StateClass state)
{
this.State = state;
}
/// <summary>
/// 对请求做处理
/// </summary>
public void Request()
{
//将当前对象作为参数传递给具体的State
State.Handle(this);
}
}
/// <summary>
/// 抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为
/// </summary>
public abstract class StateClass
{
public abstract void Handle(Context context);
}
/// <summary>
/// 具体状态类,每一个子类实现一个与Context的一个状态相关的行为
/// </summary>
public class ConcreteStateA : StateClass
{
/// <summary>
/// 设置ConcreteStateA的下一个状态是ConcreteStateB
/// </summary>
/// <param name="context"></param>
public override void Handle(Context context)
{
Console.WriteLine("当前状态是 A.");
context.State = new ConcreteStateB();
}
}
public class ConcreteStateB : StateClass
{
/// <summary>
/// 设置ConcreteStateB的下一个状态是ConcreteSateA
/// </summary>
/// <param name="context"></param>
public override void Handle(Context context)
{
Console.WriteLine("当前状态是 B.");
context.State = new ConcreteStateA();
}
}
/// <summary>
/// 客户端测试代码
/// </summary>
public void StateTest()
{
//初始化一个状态
Context Context = new Context(new ConcreteStateA());
//执行请求,并修改状态。
Context.Request();
//执行请求,并修改状态。
Context.Request();
//执行请求,并修改状态。
Context.Request();
}
优点:
•状态模式把各种状态转移逻辑分布到一系列State的子类中,来减少相互间的依赖。
•所有状态相关的行为都放在某个ConcereteState中,通过定义新的ConcereteState很容易地增加新的状态和转换。
缺点:
•状态的转换被定义在concreteState或context中,也就是说由concreteState或context决定下一个状态,状态的插入仍需要修改代码
适用场合:
•当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式来。
•一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态。
与策略模式区别:
策略模式关注行为的变化,但归根结底只有一个行为,变化的只是行为的实现.客户不关注这些.当新增变化时对客户可以没有任何影响.
状态模式同样关注行为的变化,但这个变化是由状态来驱动,一般来说每个状态和行为都不同.新增的状态或行为一般与已有的不同,客户需要关注这些变化.
状态模式中State及其子类中的操作都将Context传入作为参数,以便State可以通过这个指针调用Context中的方法,修改状态。而策略模式没有。