我們就以工程師的一天:Eat、Coding、Sleep 三種狀態,來示範 STATE 模式 STATE 模式是由以下三個部分組成:
Context:用來控制所有的狀態,其會是與客戶端的接口,客戶端只會與 Context 互動
State:定義各個狀態的抽象方法,ConcreteState 會繼承 State 並實作其方法
ConcreteState:Eat、Coding、Sleep,三種狀態的實作
定義 Context 用列舉定義狀態 因我們有三種 State,所以我用列舉定義出 State,待會 Context 裡的 GetState 方法會用到
1 2 3 4 5 6 7 8 9 10 11 12 namespace DemoCode.DesignPattern.State { public enum StateEnum { Eat, Sleep, Coding } }
定義 Context 定義客戶端的接口 Context,其為所有狀態的控制中心,客戶端只能透過它執行各狀態的行為
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 using System;namespace DemoCode.DesignPattern.State { public class SoftwareEngineer { private State _itsState; private Eat _eatState; private Sleep _sleepState; private Coding _codeingState; public SoftwareEngineer ( ) { this ._eatState = new Eat(); this ._sleepState = new Sleep(); this ._codeingState = new Coding(); this ._itsState = this ._eatState; } public string GetCurrentStateName ( ) { Console.WriteLine($"現在狀態:{this ._itsState.StateName()} " ); return this ._itsState.StateName(); } public State GetState (StateEnum state ) { switch (state) { case StateEnum.Eat: return this ._eatState; case StateEnum.Sleep: return this ._sleepState; case StateEnum.Coding: return this ._codeingState; default : throw new ArgumentException(); } } public void SetState (State state ) { this ._itsState = state; } public void Action ( ) { this ._itsState.Action(this ); } } }
定義 State 定義基底類別,各狀態類別會繼承此類別,並實作其方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 namespace DemoCode.DesignPattern.State { public abstract class State { public abstract string StateName ( ) ; public virtual void Action (SoftwareEngineer softwareEngineer ) { } } }
實作 ConcreteState 這邊放上 Eat.cs 的程式碼,其他兩種狀態差不多 每個狀態類別實作基底類別宣告的方法,並自行決定完成動作後,下個狀態為何
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 using System;namespace DemoCode.DesignPattern.State { public class Eat : State { public override string StateName ( ) { return "Eat" ; } public override void Action (SoftwareEngineer softwareEngineer ) { Console.WriteLine("該吃飯了" ); Console.WriteLine("吃完飯該寫程式了" ); var nextState = softwareEngineer.GetState(StateEnum.Coding); softwareEngineer.SetState(nextState); } } }
結果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 namespace DemoCode { class Program { static void Main (string [] args ) { var engineer = new SoftwareEngineer(); Console.WriteLine("Step01:" ); engineer.GetCurrentStateName(); engineer.Action(); Console.WriteLine("Step02:" ); engineer.GetCurrentStateName(); engineer.Action(); Console.WriteLine("Step03:" ); engineer.GetCurrentStateName(); engineer.Action(); Console.WriteLine("Step04:" ); engineer.GetCurrentStateName(); engineer.Action(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Step01: 現在狀態:Eat 該吃飯了 吃完飯該寫程式了 Step02: 現在狀態:Coding 該寫程式了 寫完程式該睡覺了 Step03: 現在狀態:Sleep 該睡覺了 睡完覺該吃飯了 Step04: 現在狀態:Eat 該吃飯了 吃完飯該寫程式了
完整程式碼請參考
結論 STATE 模式的好處是將各 State 的邏輯分離,在管理複雜的系統行為時很有幫助 而 STATE 模式與 STRATEGY 模式看起來很相近,但差別在於,前者知道各 State 的存在,且能透過 Context 進行修改,後者則相反。如下圖可看出,Context 並不知道是哪個 Contrete Strategy 實作 method 的,而 State 會知道
Strategy 模式
參考 STATE 模式.無瑕的程式碼 敏捷完整篇:物件導向原則、設計模式與 C# 實踐 wiki - state pattern 狀態模式-State Pattern 狀態模式 | State Pattern [Design Pattern] State 狀態模式