1. 程式人生 > 其它 >狀態模式(一)

狀態模式(一)

本人從事儀器儀表行的軟體開發工作,在軟體的業務邏輯中,經常需要去對儀器的執行流程進行控制,一種方法就是開啟一個while迴圈,通過迴圈不斷地去查詢狀態的值,然後在迴圈內部根據狀態值去執行特定的操作。示例程式碼如下:

 static  void Main(string[] args)
        {
           CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
           Run(StateType.A, cancellationTokenSource.Token);//阻塞了主執行緒,實際可能會開啟執行緒或者非同步去執行
        }

       

       private static void Run(StateType state, CancellationToken token)
        {
            while (token != null && !token.IsCancellationRequested)
            {
                switch (state)
                {
                    case StateType.A:
                        HandleWorkA();
                        state = StateType.B;
                        break;
                    case StateType.B:
                        HandleWorkB();
                        state = StateType.C;
                        break;
                    case StateType.C:
                        HandleWorkC();
                        state = StateType.D;
                        break;
                    case StateType.D:
                        HandleWorkD();
                        state = StateType.E;
                        break;
                    case StateType.E:
                        HandleWorkE();
                        state = StateType.A;
                        break;
                }
                Thread.Sleep(1000);
            }
        }

        private static void HandleWorkE()
        {
            Console.WriteLine("執行業務邏輯E");
        }

        private static void HandleWorkD()
        {
            Console.WriteLine("執行業務邏輯D");
        }

        private static void HandleWorkC()
        {
            Console.WriteLine("執行業務邏輯C");
        }

        private static void HandleWorkB()
        {
            Console.WriteLine("執行業務邏輯B");
        }

        private static void HandleWorkA()
        {
            Console.WriteLine("執行業務邏輯A");
        }
    }
    enum StateType
    {
        A,
        B,
        C,
        D,
        E
    }

以上程式碼開啟了一個迴圈,在迴圈中,我們通過switch case 不斷地去判斷state的值,然後處理相應的業務,在業務處理結束時,我們會更新state的值,從而實現流程的跳轉。

觀察上面的程式碼我們可以發現幾個特點:

1、狀態切換的”動力源“是while迴圈。在上述程式碼中,HandleWorkA()、HandleWorkB()等等業務邏輯之所以能夠被執行,狀態之所以能夠切換,其”動力“正是來自於while迴圈。

2、各個case下執行的程式碼邏輯是基本相似的。在每個case下,都會執行各自的業務邏輯,並且會決定下一個迴圈中switch的state值,在本例中就是去執行HandleWorkA()、HandleWorkB()等方法,在執行完這些方法後,狀態state進行了更新。

3、新的狀態,是由老的狀態決定的。在每個case的break之前,其實已經知道下一次迴圈的狀態了。比如在執行case A的業務後,我們就知道,下一個執行的必定是case B的業務。

由此我們想到,如果我們在執行case A的業務之後,直接呼叫執行case B的業務,那不就不需要while迴圈來給狀態之間的跳轉提供”動力“了嗎?那是否在caseA 的HandleWorkA()之後呼叫HandleWorkB()就可以了呢?顯然是不行的,因為如果這樣做,在呼叫完HandleWorkB()我們同樣還需要考慮執行HandleWorkC()的問題,最後程式碼就成了以下這樣:

  private static
void Run(StateType state, CancellationToken token) { while (token != null && !token.IsCancellationRequested) { switch (state) { case StateType.A: HandleWorkA(); HandleWorkB(); HandleWorkC(); HandleWorkD(); HandleWorkE(); break; } } }

究其原因,是因為我們在執行完當前case的業務之後呼叫新的case的業務時,呼叫的是具體的方法,因此,我們在程式碼中必須呼叫不同的方法。

而上面我們提到”各個case下執行的程式碼邏輯是基本相似的“,因此我們考慮將每個case下的程式碼邏輯進行抽象,從而實現統一的呼叫。由此引出了本文的主題,狀態模式,以上程式碼,使用狀態模式的實現如下:

 static  void Main(string[] args)
        {
            StateMachine stateMachine   = new StateMachine(new StateA());
            stateMachine.UpdateState();
        }

 interface IState
    {
        void Handle(StateMachine stateMachine);//把stateMachine傳到Handle方法裡,因為State業務邏輯可能需要用到stateMachine
    }

    class StateMachine
    {
        IState state;
        public StateMachine(IState initState)
        {
            this.state = initState;
        }
        public void UpdateState()
        {
            state.Handle(this);
        }
    }

   class StateA : IState
    {
        public void Handle(StateMachine stateMachine)
        {

            Console.WriteLine("執行業務邏輯A");
            Thread.Sleep(1000);
            new StateB().Handle(stateMachine);
        }
    }
    class StateB : IState
    {
        public void Handle(StateMachine stateMachine)
        {

            Console.WriteLine("執行業務邏輯B");
            Thread.Sleep(1000);
            new StateC().Handle(stateMachine);
        }
    }
    class StateC : IState
    {
        public void Handle(StateMachine stateMachine)
        {
            Console.WriteLine("執行業務邏輯C");
            Thread.Sleep(1000);
            new StateD().Handle(stateMachine);
        }
    }

    class StateD : IState
    {
        public void Handle(StateMachine stateMachine)
        {
            Console.WriteLine("執行業務邏輯D");
            Thread.Sleep(1000);
            new StateE().Handle(stateMachine);
        }
    }
    class StateE : IState
    {
        public void Handle(StateMachine stateMachine)
        {
            Console.WriteLine("執行業務邏輯E");
            Thread.Sleep(1000);
            new StateA().Handle(stateMachine);
        }
    }

對比兩種方式我們寫的程式碼,我們發現了一些改變:

1、switch case沒有了,取而代之的是多個State,我們將不同狀態要執行的業務邏輯封裝到了StateA-StateE中;

2、while迴圈沒有了,狀態跳轉的實現,是通過在每個狀態的Handle方法結束前執行新狀態的Handle方法實現的,如在StateA的Handle方法末尾,我們執行了new StateB().Handle(stateMachine);

 

那麼在本例中使用狀態模式的好處是什麼呢?我概括為以下幾個方面:

1、對不同狀態的業務邏輯進行了封裝,程式碼邏輯的封裝性更好

2、去掉了while 和switch case,在新增狀態時,只需要定義新的狀態類就可以,程式碼的主呼叫過程( StateMachine stateMachine = new StateMachine(new StateA()); stateMachine.UpdateState();)不變,因而拓展性更好

3、swit case需要判斷所有可能的狀態,而在State類中,我們只會關注和State相關的狀態,如在StateA中我們只關注StateA執行的業務邏輯DoWorkA,以及它的後續狀態StateB。

 

本文中stateMachine.UpdateState();在運行了之後,再沒有接受外部的控制,狀態的跳轉是通過狀態的業務邏輯控制的,是自驅動的,那如何讓外部業務邏輯來驅動狀態跳轉呢?這一問題將在下一篇文章中介紹。