小话设计模式(番外三)有限状态机模式

有限状态机(Finite State Machine)定义了一系列状态,对象可以在这些状态之间有条件的转换,并且定义了一个状态机,这个负责管理所有的状态和状态之间的转换条件。一般使用事件来作为转换条件,而具体的转换规则由用户来制定。

看起来跟状态模式有些类似,但其实还是有很大区别的。我们不妨再以小话设计模式(二十)状态模式中的例子为例,看一下二者有什么区别。

有这样一个游戏,主角身体里住着三个灵魂,它可以切换成对应的三种形态,来施放不同的技能。

灵魂声明:

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;}
	}

在每个状态的OnEnter里为hero切换灵魂。

最后是英雄的实现:

	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、状态转换的规则可以很灵活。

状态机模式的缺点很明显,就是需要编写复杂的转换规则。

你可能感兴趣的:(设计模式,小话设计模式)