2013年2月22日 星期五

Design Pattern : State狀態模式


Design Pattern : State
作者: JB

State pattern
使用的時機及範例,可以網路上很多文章。
主要是使用在有順序的事件上,可以免除if…else…swith…case…的程式碼。
當然缺點就是要多一層去做State管理,以及多出多支對應的State class


這邊會以實務上:依序做四次電文的傳送為範例。
每一次的電文傳送就是一種狀態(State)

1.      先定義四種電文的類型(State種類)
/// <summary>
    /// 狀態種類
    /// </summary>
    public enum StateKind
    {
        /// <summary>
        /// 流程一
        /// </summary>
        A1,
        /// <summary>
        /// 流程二
        /// </summary>
        A2,
        /// <summary>
        /// 流程三
        /// </summary>
        T1,
        /// <summary>
        /// 流程四
        /// </summary>
        T2
    }
2.       定義State Pattern的核心類別:State 以及 Context
State : abstract class,
負責設定Context內容,以及設定某種狀態下需要執行的工作。
Context
:負責存放目前執行的狀態:State,以及狀態種類:StateKind
       
也負責設定哪個State為第一個要執行,和啟動目前狀態下需要執行的工作。

/// <summary>
///
State
/// </summary>
public abstract class State
{

    /// <summary>
        /// 執行目前的狀態要做的事情(DoAction),並將目前狀態設定給下一個狀態
        /// </summary>
        /// <param name="context">Context類別</param>
        /// <returns>訊息</returns>
        public abstract String GetState(Context context);

        /// <summary>
        ///  執行目前的狀態要做的事情
        /// </summary>
        public abstract void DoAction(Context _context);
    }

/// <summary>
    /// Context
    /// </summary>
    public class Context
    {
        //目前的狀態類別
        private StateKind _stateKind;
        //存放狀態
        private State _state;

        /// <summary>
        /// 建構
        /// </summary>
        public Context()
        {
            this._state = new A1_State();  //第一個要執行的State
        }

        /// <summary>
        /// 目前的狀態類別
        /// </summary>
        public StateKind StateKind
        {
            get { return this._stateKind; }
            set { this._stateKind = value; }
        }

        /// <summary>
        /// 存放狀態
        /// </summary>
        public State State
        {
            get { return this._state; }
            set { this._state = value; }
        }
      
        /// <summary>
        /// 執行State.GetState ...
        /// </summary>
        /// <returns>訊息</returns>
        public String GetState()
        {
            return _state.GetState(this);
        }
    }

3.      看到這邊,我們應該知道了,只要一直把State存放在Context,做完一個State時,告訴Context換下一個State再繼續做就可以了。
所以我們開始來實作四種State的程式碼… (這邊只列出一個)

/// <summary>
    /// 狀態 : A1
    /// </summary>
    public class A1_State:State
    {
        /// <summary>
        /// 執行目前的狀態要做的事情(DoAction),並將目前狀態設定給下一個狀態
        /// </summary>
        /// <param name="context"></param>
        /// <returns>訊息</returns>
        public override String GetState(Context _context)
        {
            if (_context.StateKind == StateKind.A1)
            {
                //執行此狀態要做的事
                this.DoAction(_context);
               
                //設定下一個狀態為Auth2
                _context.State = new A2_State();
                _context.StateKind = StateKind.A2;
            }
            else //非此State對應的StateKind
            {
                //...
            }
            return "A1流程結束...";
        }

        /// <summary>
        /// 執行工作
        /// </summary>
        /// <param name="_context">Context物件</param>
        public override void DoAction(Context _context)
        {
           //Step3. 傳送電文
           Console.WriteLine("開始傳送A1電文");
        }
    }
4.      各自完成了A1A2T1T2的覆寫State程式碼後,
接下來只要在主程式,宣告一個Context物件來啟動紀錄在其中的State動作即可。
請注意主程式並不用知道傳送電文的順序, 主程式只要知道總共有幾個電文要送就行了。

Context
_context = new Context();
   
String step1Msg = _context.GetState();
Console.WriteLine(step1Msg);

String step2Msg = _context.GetState();
Console.WriteLine(step2Msg);

String step3Msg = _context.GetState();
Console.WriteLine(step3Msg);

String step4Msg = _context.GetState();
Console.WriteLine(step4Msg);


執行結果 …………

開始傳送A1電文
A1
流程結束...
開始傳送A2電文
A2
流程結束...
開始傳送T1電文
T1
流程結束...
開始傳送T2電文
T2
流程結束...


沒有留言:

張貼留言