.NET - A Simple Finite State Machine Solution
想要用實現有限狀態機的功能,看了一下網路上的解決方案以及 State Pattern,覺得都不怎麼適用,因此利用 Tuple 與 Dictionary 去實作了一個簡易又可重複使用的 State Machine:
public sealed class StateMachine<TState, TCommand>
{
#region Fields
Dictionary<Tuple<TState, TCommand>, TState> _transitions;
#endregion Fields
#region Properties
/// <summary>
/// Gets the m_ transitions.
/// </summary>
/// <value>
/// The m_ transitions.
/// </value>
private Dictionary<Tuple<TState, TCommand>, TState> m_Transitions
{
get
{
return _transitions ?? (_transitions = new Dictionary<Tuple<TState, TCommand>, TState>());
}
}
/// <summary>
/// Gets the state of the current.
/// </summary>
/// <value>
/// The state of the current.
/// </value>
public TState CurrentState { get; private set; }
#endregion Properties
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="StateMachine{TState, TCommand}"/> class.
/// </summary>
/// <param name="state"> The state.</param>
public StateMachine(TState state)
{
this.CurrentState = state;
}
#endregion Constructors
#region Methods
/// <summary>
/// Adds the translation.
/// </summary>
/// <param name="baseState"> State of the base.</param>
/// <param name="command"> The command.</param>
/// <param name="targetState"> State of the target.</param>
/// <returns></returns>
public StateMachine<TState, TCommand> AddTranslation(TState baseState, TCommand command, TState targetState)
{
var key = new Tuple<TState, TCommand>(baseState, command);
if (m_Transitions.ContainsKey(key))
throw new Exception("Duplicated translation!!");
m_Transitions.Add(key, targetState);
return this;
}
/// <summary>
/// Triggers the command.
/// </summary>
/// <param name="command"> The command.</param>
public void Trigger(TCommand command)
{
var key = new Tuple<TState, TCommand>(this.CurrentState, command);
if (!m_Transitions.ContainsKey(key))
throw new Exception("Translation not found!!");
var state = m_Transitions[key];
this.CurrentState = state;
}
#endregion Methods
}
使用時只要宣告狀態的列舉以及用來觸發狀態轉換的命令列舉,然後帶入初始狀態去建立狀態機的物件實體。接著設定狀態機內所內含的狀態轉換,設定時需指定觸發時的狀態、用來觸發的命令、以及轉換後的狀態。最後在程式中適當的時機點插入觸發適當的命令即可,程式寫起來會像下面這樣:
public enum Command
{
Next,
Reset
}
public enum State
{
State1,
State2,
State3
}
...
var stateMachine = new StateMachine<State, Command>(State.State1)
.AddTranslation(State.State1, Command.Reset, State.State1)
.AddTranslation(State.State2, Command.Reset, State.State1)
.AddTranslation(State.State3, Command.Reset, State.State1)
.AddTranslation(State.State1, Command.Next, State.State2)
.AddTranslation(State.State2, Command.Next, State.State3);
Console.WriteLine(stateMachine.CurrentState);
stateMachine.Trigger(Command.Next);
Console.WriteLine(stateMachine.CurrentState);
stateMachine.Trigger(Command.Reset);
Console.WriteLine(stateMachine.CurrentState);
...