简介
状态模式(State Pattern),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态装换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
状态模式在工作流或游戏等各种系统中大量使用。
角色
- 环境角色(Context):也称上下文,定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
- 抽象状态角色(State):定义一个接口,用以封装环境对象的一个特定的状态所对应的行为。
- 具体状态角色(ConcreteState):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
优点
- 封装了转换规则。
- 枚举可能的状态,在枚举状态之前需要确定状态种类。
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
应用场景
- 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
- 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
实现
// 状态接口
public interface IState
{void Handle(Context context);
}// 实现状态接口的具体状态类
public class ConcreteStateA : IState
{public void Handle(Context context){// 处理状态A的逻辑Console.WriteLine("当前状态是状态A");// 可能会改变状态context.State = new ConcreteStateB();}
}public class ConcreteStateB : IState
{public void Handle(Context context){// 处理状态B的逻辑Console.WriteLine("当前状态是状态B");// 可能会改变状态context.State = new ConcreteStateA();}
}// 维护状态的上下文类
public class Context
{private IState _state;public Context(IState state){this._state = state;}public IState State{get { return _state; }set { _state = value; }}public void Request(){_state.Handle(this);}
}// 客户端代码
class Program
{static void Main(string[] args){Context context = new Context(new ConcreteStateA());// 客户端请求,状态对象将根据当前状态处理请求context.Request();context.Request();context.Request();Console.ReadKey();}
}
在这个案例中,Context
类维护了一个IState
类型的实例,代表对象的当前状态。IState
接口定义了状态对象需要实现的行为。ConcreteStateA
和ConcreteStateB
是具体的状态类,实现了IState
接口,并在Handle
方法中实现了在该状态下的具体行为。客户端代码通过调用context.Request()
来触发状态的变化。每次调用都会导致状态对象改变它的行为。这个例子展示了如何使用状态模式来管理和切换对象的内部状态。