厳密に言えば、古典的な GoF State パターンを実装している場合、State サブクラス自体が State 遷移を認識して実行する責任があります。State の所有者は遷移を管理する責任を負わず、パターンの意図の大部分は、状態遷移の動作を State オブジェクトにカプセル化し、クライアントがそれらに委譲することです。各 State サブクラスのインスタンスが 1 つだけであることを保証する Factory を導入して、状態間を行き来するときに同じインスタンスが再利用されるようにしました。
public abstract class State
{
protected StateFactory _factory;
protected IStateUser _context;
public State(StateFactory factory, IStateUser context)
{
_factory = factory;
_context = context;
}
protected void TransitionTo<T>(Func<T> creator) where T : State
{
State state = _factory.GetOrCreate<T>(creator);
_context.CurrentState = state;
}
public abstract void MoveNext();
public abstract void MovePrevious();
}
public class State1 : State
{
public State1(StateFactory factory, IStateUser context)
: base(factory, context)
{
}
public override void MoveNext()
{
TransitionTo<State2>(() => new State2(_factory, _context));
}
public override void MovePrevious()
{
throw new InvalidOperationException();
}
}
public class State2 : State
{
public State2(StateFactory factory, IStateUser context)
: base(factory, context)
{
}
public override void MoveNext()
{
TransitionTo<State3>(() => new State3(_factory, _context)); //State 3 is omitted for brevity
}
public override void MovePrevious()
{
TransitionTo<State1>(() => new State1(_factory, _context));
}
}
public interface IStateUser
{
State CurrentState { get; set; }
}
public class Client : IStateUser
{
public Client()
{
var factory = new StateFactory();
var first = new State1(factory, this);
CurrentState = factory.GetOrCreate<State1>(() => first);
}
public void MethodThatCausesTransitionToNextState()
{
CurrentState.MoveNext();
}
public void MethodThatCausesTransitionToPreviousState()
{
CurrentState.MovePrevious();
}
public State CurrentState
{
get;
set;
}
}
public class StateFactory
{
private Dictionary<string, State> _states = new Dictionary<string, State>();
public State GetOrCreate<T>(Func<T> creator) where T : State
{
string typeName = typeof(T).FullName;
if (_states.ContainsKey(typeName))
return _states[typeName];
T state = creator();
_states.Add(typeName, state);
return state;
}
}