设计模式-状态模式(State)
定义
“让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了类一样”
实现
类图
说明
- Context(状态拥有者)
具有“状态”属性的类,可以让外界能够得知状态的改变或通过操作让状态改变; - State(状态接口类)
定义状态的接口,负责规范Context在特定状态下的表现行为; - ConcreteStateX(具体状态类)
继承自状态类,表示Context状态拥有者所包含的状态;
状态转换有两种方式,
- 交由Context本身,按条件在各状态之间转换;
- 产生Context对象是,指定一个初始状态,而在后续执行过程中的状态转换则交由State对象负责,Context不再介入;
代码
以下代码在unity环境下编写运行
using System.Collections.Generic;
using UnityEngine;
namespace ZhhDesignPattern
{
//此例通过一个值value来确定状态,
//状态A:value > 0 && value <= 5;
//状态B:value > 5 && value <= 10;
//状态C:value > 10,
//状态c添加一个进入条件,以演示状态模式的一个处理优点(不同状态的处理放到对应状态中去),只有当处于状态b时才可以通过value > 10 来进入状态c,而处于其他状态时则无法变更
public class State_UnitTest : MonoBehaviour
{
private Context m_context_test;
void Awake()
{
m_context_test = new Context();
}
void Start()
{
m_context_test.Request(2);
Debug.Log(m_context_test.ShowCruuentStateName());
m_context_test.Request(12);//当前状态并非处于状态b无法转换到状态c
Debug.Log(m_context_test.ShowCruuentStateName());
m_context_test.Request(7);
Debug.Log(m_context_test.ShowCruuentStateName());
m_context_test.Request(12);//当前状态处于状态b可以转换到状态c
Debug.Log(m_context_test.ShowCruuentStateName());
}
}
//状态拥有者
public class Context
{
public Context()
{
allState = new Dictionary<StateName, State>();
allState.Add(StateName.State_A, new ConcreteState_A(this));
allState.Add(StateName.State_B, new ConcreteState_B(this));
allState.Add(StateName.State_C, new ConcreteState_C(this));
SetState(allState[StateName.State_A]);//设置一个初始状态
}
public State currentState = null;//当前状态
public Dictionary<StateName, State> allState;//所有状态
//状态拥有者对状态的请求函数,调用当前状态的处理函数,呈现各自不同的状态行为
public void Request(int value)
{
currentState.Handle(value);
}
//设置状态,变更当前状态
public void SetState(State settingState)
{
if (settingState == currentState)
return;
currentState = settingState;
}
//返回当前状态信息
public string ShowCruuentStateName()
{
if (currentState == null)
return "当前状态为空";
return currentState.GetStateName();
}
}
//状态接口类
public abstract class State
{
public State(Context context)
{
m_context = context;
}
protected Context m_context;//状态拥有者
public abstract void Handle(int value);//处理函数
public abstract string GetStateName();
}
public class ConcreteState_A : State
{
public ConcreteState_A(Context context) : base(context)
{
}
public override string GetStateName()
{
return "状态为A";
}
//进行状态处理
public override void Handle(int value)
{
if (value > 0 && value <= 5)
{
m_context.SetState(m_context.allState[StateName.State_A]);
}
else if (value > 5 && value <= 10)
{
m_context.SetState(m_context.allState[StateName.State_B]);
}
}
}
public class ConcreteState_B : State
{
public ConcreteState_B(Context context) : base(context)
{
}
public override string GetStateName()
{
return "状态为B";
}
public override void Handle(int value)
{
if (value > 0 && value <= 5)
{
m_context.SetState(m_context.allState[StateName.State_A]);
}
else if (value > 5 && value <= 10)
{
m_context.SetState(m_context.allState[StateName.State_B]);
}
//只有处于状态B时,且value>10,才可以转换到c状态
else if (value > 10)
{
m_context.SetState(m_context.allState[StateName.State_C]);
}
}
}
public class ConcreteState_C : State
{
public ConcreteState_C(Context context) : base(context)
{
}
public override string GetStateName()
{
return "状态为C";
}
public override void Handle(int value)
{
if (value > 0 && value <= 5)
{
m_context.SetState(m_context.allState[StateName.State_A]);
}
else if (value > 5 && value <= 10)
{
m_context.SetState(m_context.allState[StateName.State_B]);
}
}
}
public enum StateName
{
State_A,
State_B,
State_C,
}
}
小结
使用状态模式的优点:
- 减少错误的发生并降低维护难度,不使用switch代码块来进行状态判断,可以减少增加状态后的维护难度
- 执行状态环境单一化,与每一个状态相关的对象及操作都在一个状态类下,可以清楚了解每一个状态的执行所需要的对象及配合的类
缺点:
- 如果状态模式应用在有大量状态的系统时,就会出现产生过多的类的情况,此时会伴随这类爆炸的问题