2014年09月25日

ステート

State

ステートとは状態のこと。
状態を持つクラスを機能毎に定義して、それをコンテクストに保持させる。
使う側は状態を意識せずにコンテクストの同じメソッドを呼ぶ。
実装コストの割に合わないケースが多いのか、あまり使われていない気がする。

サンプルコード

基本構成例。
Stateを継承したクラスが2種ある。使う側はコンテクストのメソッドのみを使用する。
   // 状態クラス用のインタフェース定義
    public interface State
    {
         void Do(Context context);
    }
    // 状態Aクラス
    public class StateA : State
    {
        public void Do(Context context)
        {
            Console.WriteLine("Do something in StateA");
            context.State = new StateB();
        }
    }
    // 状態Bクラス
    public class StateB : State
    {
        public void Do(Context context)
        {
            Console.WriteLine("Do something in StateB");
            context.State = new StateA();
        }
    }
    // コンテクストクラス
    public class Context
    {
        public State State { get; set; }

        public void Execute()
        {
            State.Do(this);
        }
    }


これを使う側のコード例
    class Tester
    {
        static void Main(string[] args)
        {
            var context = new Context() { State = new StateA() };

            context.Execute(); // StateA.Do()
            context.Execute(); // StateB.Do()
            context.Execute(); // StateA.Do()
         }
     }


サンプルその2

教室事例的ですが、ちょっと具体的な例。
状態として金額を持ちます。
閾値以下なら貧乏状態、以上なら金持ち状態です。
金持ちなら3000円ランチ、貧乏なら500円ランチを食べます。
500円もないと何も食べられません。

    // 状態
    abstract class State
    {
        // 金持ちと貧乏との閾値
        protected const int threshold = 10000;
        // 所持金  値変更時(Setter)に状態チェックを実行します
        private int _amount;
        public int Amount { get { return _amount; } set { _amount = value; Check(); } }
        // コンテキスト
        public Context Context { get; set; }
        // 食事する
        public abstract void Lanch();
        protected abstract void Check();
    }
    // 貧乏状態
    class PoorState : State
    {
        public PoorState(Context context)
        {
            Context = context;
        }
        public PoorState(State state)
        {
            Context = state.Context;
            Amount = state.Amount;
        }
        public override void Lanch()
        {
            if(Amount < 500)
            {
                Console.WriteLine("昼食なし");
            }
            else
            {
                Console.WriteLine("500円ランチ");
                Amount = Amount - 500;
            }
        }
        protected override void Check()
        {
            if (Amount >= threshold)
            {
                Context.State = new RichState(this);
            }
        }
    }
   // 金持ち状態
    class RichState : State
    {
        public RichState(Context context)
        {
            Context = context;
        }
        public RichState(State state)
        {
            Context = state.Context;
            Amount = state.Amount;
        }
        public override void Lanch()
        {
            Console.WriteLine("3000円ランチ");
            Amount = Amount - 3000;   
        }
        protected override void Check()
        {
            if (Amount < threshold)
            {
                Context.State = new PoorState(this);
            }
        }
    }
    // コンテキスト
    class Context
    {
        public State State { get; set; }

        public Context()
        {
            State = new PoorState(this);
        }

        public void AddAllowance(int amount)
        {
            State.Amount += amount;

            Console.WriteLine("{0}円追加。残金{1}円",amount, State.Amount);
        }

        public void EatLanch()
        {
            State.Lanch();

            Console.WriteLine("残金{0}円", State.Amount);
        }
    }

これを使う側のコード
    class Tester
    {
        static void Main(string[] args)
        {
           var context = new Context();
            context.EatLanch();           // 昼飯なし    残金0円
            context.AddAllowance(3000);   // 3000円追加  残金3000円
            context.EatLanch();           // 500円ランチ 残金2500円
            context.AddAllowance(8000);   // 8000円追加  残金10500円
            context.EatLanch();           // 3000円ランチ 残金7500円 
            context.EatLanch();           // 500円ランチ 残金7000円
        }
    }

posted by RR at 00:30 | Comment(0) | デザインパターン | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。