引用自《C#设计模式》
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中的某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。
状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
状态模式的结构与实现
状态模式的结构
状态模式包含以下三个角色。
(1)Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性,且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
(2)State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现了这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
(3)ConcreteState(具体状态类):它是抽象状态类的子类,每一个具体状态类实现一个与环境类的一个状态相关的行为,对应环境类的一个具体状态,不同的具体状态类其行为有所不同。
状态模式的实现
在状态模式中,将对象在不同状态下的行为封装到不同的状态类中,为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,需要对状态进行抽象化,引入了抽象状态类角色。其典型代码如下:
abstract class State
{
//声明抽象业务方法,不同的具体状态类可以有不同的实现
public abstract void Handle();
}
在抽象状态类的子类(即具体状态类)中实现了在抽象状态类中声明的业务方法,不同的具体状态类可以提供完全不同的方法实现。实际使用时,在一个状态类中可能包含多个业务方法,如果在具体状态类中某些业务方法的实现完全相同,则可以将这些方法移至抽象状态类,实现代码的复用。典型的具体状态类代码如下:
class ConcreteState : State
{
public override void Handle()
{
//方法具体实现代码
}
}
环境类维持一个对抽象状态类的引用,通过SetState()方法可以向环境类注入不同的状态对象,再在环境类的业务方法中调用状态对象的方法。其典型代码如下:
class Context
{
private State state; //维持一个对抽象状态对象的引用
private int value; //其他属性值,该属性值的变化可能会导致对象的状态发生变化
//设置状态对象
public void SetState(State state)
{
this.state = state;
}
public void Request()
{
//其他代码
state.Handle(); //调用状态对象的业务方法
//其他代码
}
}
环境类实际上是真正拥有状态的对象,只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。
在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式。
(1)统一由环境类来负责状态之间的转换,此时,环境类还充当了状态管理器(State Manager)角色,在环境类的业务方法中通过对某些属性值的判断实现状态转换,也可以提供一个专门的方法用于实现属性判断和状态转换,代码片段如下:
…
public void ChangeState()
{
//判断属性值,根据属性值进行状态转换
if(value == 0)
{
this.SetState(new ConcreteStateA());
}
else if(value == 1)
{
this.SetState(new ConcreteStateB());
}
……
}
…
(2)由具体状态类来负责状态之间的转换,可以在具体状态类的业务方法中判断环境类的某些属性值,再根据情况为环境类设置新的状态对象,实现状态转换。同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间将存在依赖或关联关系,因为状态类需要访问环境类中的属性值,具体状态类ConcreteStateA的代码片段如下:
…
public void ChangeState(Context ctx)
{
//根据环境对象中的属性值进行状态转换
if(ctx.value == 1)
{
ctx.SetState(new ConcreteStateB());
}
else if(value == 2)
{
ctx.SetState(new ConcreteStateC());
}
}
…
状态模式的应用实例
开发一个放大镜工具,其具体功能描述如下:
用户单击“放大镜”按钮之后屏幕将放大一倍,再单击一次“放大镜”按钮屏幕再放大一倍,第三次单击该按钮后屏幕将还原到默认大小。
//Screen.cs屏幕类
using system;
namespace ScreenStateSample
{
class Screen
{
//枚举所有的状态,currentState表示当前状态
private ScreenState currentState, normalState, largerState, largestState;
public Screen()
{
this.normalState = new NormalState(); //创建正常状态对象
this.largerState = new LargerState(); //创建二倍放大状态对象
this.largestState = new LargestState(); //创建四倍放大状态对象
this.currentState = normalState; //设置初始状态
this.currentState.Display();
}
public void SetState()
{
this.currentState = state;
}
//单击事件处理方法,封装了对状态类中业务方法的调用和状态的转换
public void OnClick()
{
if(this.currentState == normalState)
{
this.SetState(largerState);
this.currentState.Display();
}
else if(this.currentState == largerState)
{
this.SetState(largestState);
this.currentState.Display();
}
else if(this.currentState == largestState)
{
this.SetState(normalState);
this.currentState.Display();
}
}
}
}
---------------------------------------------------------------------------------------
//ScreenState.cs 抽象状态类
using system;
namespace ScreenStateSample
{
abstract class ScreenState
{
public abstract void Display();
}
}
---------------------------------------------------------------------------------------
//NormalState.cs 正常状态类
using system;
namespace ScreenStateSample
{
class NormalState : ScreenState
{
public override void Display()
{
Console.WriteLine("正常大小");
}
}
}
---------------------------------------------------------------------------------------
//LargerState.cs 二倍状态类
using system;
namespace ScreenStateSample
{
class LargerState: ScreenState
{
public override void Display()
{
Console.WriteLine("二倍大小");
}
}
}
---------------------------------------------------------------------------------------
//LargestState.cs 四倍状态类
using system;
namespace ScreenStateSample
{
class LargestState: ScreenState
{
public override void Display()
{
Console.WriteLine("四倍大小");
}
}
}