C#でシンプルなStatePatternを組んだ
StatePattern
デザインパターンの一角, StatePatternをC#で組んでみました.
Stateパターンを使えば, 呼び出し側のコードでif文/switch-case文を書かなくて良いというメリットがあります.
今回は, 汎用的に使えるState/Contextのクラスを作ってみます.
ソースはこちら
State
public class State
{
public StateContext Context
{
get;
private set;
}
public delegate void stateEnterEvent();
public stateEnterEvent OnEnter;
public delegate void stateExitEvent();
public stateEnterEvent OnExit;
public State(StateContext context)
{
Context = context;
}
}
ベースとなるステートクラスでは, 「自分のステートに遷移した時」と「自分のステートから出て行った時」のイベントを用意しています.
Context
各Stateを管理して, Stateの遷移を管理・実行するクラスです.
public class StateContext
{
public List<State> StateList = new List<State>();
public State CurrentState { get; private set; }
// Allow transit to self state
public bool SelfTransit = true;
object Locker = new object();
public void setCurrentState(State state)
{
if (state == null || !StateList.Contains(state))
return;
CurrentState = state;
}
public void addState(State state)
{
if (state == null || StateList.Contains(state))
return;
StateList.Add(state);
}
public void transitState(State targetState)
{
if (targetState == null || (StateList.Contains(targetState) && SelfTransit))
{
return;
}
lock (Locker)
{
CurrentState?.OnExit();
CurrentState = targetState;
CurrentState?.OnEnter();
}
}
}
状態の追加には「addState」, 遷移には「transitState」を呼び出せばOKです.
使い方
State/StateContextを継承した自作State/自作Contextを作り, AddStateを使ってStateを登録していきます.
遷移時のイベントは「onEnter」「onExit」に登録可能です.
詳しいコードはGitHubに上げているので, そちらをご覧ください.
一つ一つのStateが大きな場合
↓のように, 具象クラスを別ファイルで実装してみると良いと思います.
public class LargeState : State
{
public LargetState(LargetContext context, LargeState next) : base(context)
{
...
}
}
有効利用できそうなケース
UnityでUI組むときとかに使えそうです.
UIの場合, ユーザからの入力情報は変わらないけど, 階層構造をもっていて画面遷移する事が多いです.
Stateに応じた入力処理・画面遷移を組めば, 呼び出し側のコード(ビヘイビア)は綺麗に保てそうです.
StatePatternを使う上での注意点
呼び出すメソッドが共通化できる時に使わないと, 却ってコードの煩雑化を招きます
例えば, こんなケース.
public class StateA : State
{
public void myOwnMethodA();
}
public class StateB : State
{
public void myOwnMethodB();
}
...
public class MyContext : Context
{
public void onInput()
{
if (CurrentState.is(StateA))
{
(CurrentState as StateA).myOwnMethodA();
}
...
}
}
StateAとStateBの間で異なるインタフェースしか提供されないと, 結局Context側で無駄な条件分岐が発生します.
本来やりたかったのは「呼び出し側のコードがif文/switch-case文を使いたくない」という事なので, 本末転倒です.
「あ, このメソッドは共通化できるな」という見通しが立ってから使った方がよさそうです.
(...という認識であってるかなー? ご意見あれば, コメント欄に書き込んで下さると勉強になります)
Author And Source
この問題について(C#でシンプルなStatePatternを組んだ), 我々は、より多くの情報をここで見つけました https://qiita.com/nossey/items/f155c8732ae8b914df0a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .