看起来跟状态模式有些类似,但其实还是有很大区别的。我们不妨再以小话设计模式(二十)状态模式中的例子为例,看一下二者有什么区别。
有这样一个游戏,主角身体里住着三个灵魂,它可以切换成对应的三种形态,来施放不同的技能。
灵魂声明:
namespace HeroFSM
{
public interface ISoul
{
void CastSkill(Hero hero);
}
public class Mage : TSingleton, ISoul
{
public void CastSkill()
{
Console.WriteLine ("Summon a box");
}
}
public class Hunter : TSingleton, ISoul
{
public void CastSkill()
{
Console.WriteLine ("Fire arrow");
}
}
public class Warrior : TSingleton, ISoul
{
public void CastSkill()
{
Console.WriteLine ("Power of Thor");
}
}
}
为了保证每个灵魂只有一个实例,所以我们使用了泛型单例(参考小话设计模式(一)单例模式):
public class TSingleton where T : class, new() {
static T instance;
static readonly object syncObj = new object();
public static T GetInstance()
{
if (instance == null) {
lock (syncObj) {
if (instance == null) {
instance = new T ();
}
}
}
return instance;
}
public static void PurgeInstance()
{
instance = null;
}
}
英雄的定义我们稍后再实现,我们先看看状态机的定义:
public interface IStateObject
{
}
public interface IState
{
void OnEnter ();
void OnExit ();
bool CanReach ();
IStateObject StateObject{get;set;}
}
public class FSM {
IStateObject _stateObj;
IState _curState;
Dictionary> _stateDict = new Dictionary> ();
Dictionary _sharedStates = new Dictionary();
public FSM(IStateObject stateObj_)
{
_stateObj = stateObj_;
}
public void Start() where T:IState, new()
{
var t = typeof(T);
_curState = GetState (t);
_curState.OnEnter ();
}
public void AddTransition (string eventName) where T1:IState, new() where T2:IState, new()
{
var t1 = typeof(T1);
var t2 = typeof(T2);
if (t1 == t2) {
return;
}
IState state1 = GetState (t1);
IState state2 = GetState (t2);
if (!_stateDict.ContainsKey (state1)) {
_stateDict [state1] = new Dictionary ();
}
_stateDict [state1] [eventName] = state2;
}
public void StateTransition(string eventName)
{
if (_curState == null) {
throw new System.Exception ("Current state is null!");
}
var curStateDict = _stateDict [_curState];
if (!curStateDict.ContainsKey (eventName)) {
return;
}
IState newState = curStateDict [eventName];
if (newState.CanReach ()) {
_curState.OnExit ();
_curState = newState;
_curState.OnEnter ();
}
}
private IState GetState(System.Type t)
{
if (!_sharedStates.ContainsKey (t)) {
_sharedStates [t] = System.Activator.CreateInstance (t) as IState;
_sharedStates [t].StateObject = _stateObj;
}
return _sharedStates [t];
}
}
主要是两个方法:AddTransition和StateTransition,前者定义转换规则,后者执行状态装换。
注:为了保证每个状态都只有一个实例,所以在GetState方法里使用了享元模式来获取状态实例(参考小话设计模式(十一)享元模式)。
接着实现不同的状态:
public class MageState : IState
{
public void OnEnter()
{
Hero hero = StateObject as Hero;
hero.ChageState (Mage.GetInstance ());
}
public void OnExit()
{
}
public bool CanReach()
{
return true;
}
public IStateObject StateObject{ get; set;}
}
public class HunterState : IState
{
public void OnEnter()
{
Hero hero = StateObject as Hero;
hero.ChageState (Hunter.GetInstance ());
}
public void OnExit()
{
}
public bool CanReach()
{
return true;
}
public IStateObject StateObject{ get; set;}
}
public class WarriorState : IState
{
public void OnEnter()
{
Hero hero = StateObject as Hero;
hero.ChageState (Warrior.GetInstance ());
}
public void OnExit()
{
}
public bool CanReach()
{
return true;
}
public IStateObject StateObject{ get; set;}
}
最后是英雄的实现:
public class Hero : IStateObject
{
ISoul _soul;
FSM _fsm;
public const string MageStateEvent = "MageState";
public const string HunterStateEvent = "HunterState";
public const string WarriorStateEvent = "WarriorState";
public Hero()
{
_fsm = new FSM (this);
_fsm.Start ();
_fsm.AddTransition (MageStateEvent);
_fsm.AddTransition (HunterStateEvent);
_fsm.AddTransition (MageStateEvent);
_fsm.AddTransition (WarriorStateEvent);
_fsm.AddTransition (WarriorStateEvent);
_fsm.AddTransition (HunterStateEvent);
}
public void CastSkill()
{
_soul.CastSkill ();
}
public void ChageState(ISoul soul)
{
_soul = soul;
}
public void ChangeSoulState(string soulEvent)
{
_fsm.StateTransition (soulEvent);
}
}
HeroFSM.Hero hero = new HeroFSM.Hero();
hero.ChangeSoulState (HeroFSM.Hero.MageStateEvent);
hero.CastSkill ();
状态机模式的优点在于:
1、可以让不同的状态之间进行切换,并为这些状态切换之间添加统一的管理。
2、可以事先规定好状态转换规则,用户在执行状态转换时就不需要再去考虑这些规则。
3、可以有效的限制状态转换,因为转换事件只有在当前状态的事件表里才能被响应。
4、状态转换的规则可以很灵活。
状态机模式的缺点很明显,就是需要编写复杂的转换规则。