狀態模式容許一個對象在其內部狀態改變時改變它的行爲。用電梯來舉例,電梯能夠認爲具備開門、關門、運行、中止四種狀態,這四種狀態之間的切換具備多種限制,好比在開門狀態下不電梯不能運行,只能轉爲關門狀態;在運行狀態下,電梯只能轉爲中止狀態...
設想一下,若是要常規的if-else或者switch-case描述電梯的這幾種狀態間的切換,將生成很是複雜的、邏輯相互交織的代碼,可讀性差且不易維護。設計模式
而若是用狀態模式來實現,會是怎樣的呢?
首先建立LiftState,表明抽象的電梯狀態,包含了電梯的四個動做(方法),經過這些方法能夠切換到對應的狀態。app
public abstract class LiftState { protected Context context; public void SetContext(Context context) { this.context = context; } public abstract void Open(); public abstract void Close(); public abstract void Run(); public abstract void Stop(); }
Context是上下文類,它的做用是串聯各個狀態的過渡,在LiftSate抽象類中把Context類角色聚合進來,並傳遞到子類,這樣4個具體的實現類中本身根據環境來決定如何進行狀態的過渡。ide
public class Context { public readonly static OpenningState openningState = new OpenningState(); public readonly static ClosingState closingState = new ClosingState(); public readonly static RunningState runningState = new RunningState(); public readonly static StoppingState stoppingState = new StoppingState(); private LiftState liftState; public LiftState LiftState { get { return liftState; } set { liftState = value; liftState.SetContext(this); } } public void Open() { this.liftState.Open(); } public void Close() { this.liftState.Close(); } public void Run() { this.liftState.Run(); } public void Stop() { this.liftState.Stop(); } }
接下來是四個具體的狀態類,負責狀態之間的切換和控制,以OpenningState爲例,只能切換到Closing狀態,其它切換狀態的方法都是空實現。this
public class OpenningState : LiftState { public override void Close() { base.context.LiftState = Context.closingState; base.context.LiftState.Close(); } public override void Open() { Console.WriteLine("Openning"); } public override void Run() { // } public override void Stop() { // } } public class ClosingState : LiftState { public override void Close() { Console.WriteLine("Closing"); } public override void Open() { base.context.LiftState = Context.openningState; base.context.LiftState.Open(); } public override void Run() { base.context.LiftState = Context.runningState; base.context.LiftState.Run(); } public override void Stop() { base.context.LiftState = Context.stoppingState; base.context.LiftState.Stop(); } } public class RunningState : LiftState { public override void Close() { // } public override void Open() { // } public override void Run() { Console.WriteLine("Running"); } public override void Stop() { base.context.LiftState = Context.stoppingState; base.context.LiftState.Stop(); } } public class StoppingState : LiftState { public override void Close() { // } public override void Open() { base.context.LiftState = Context.openningState; base.context.LiftState.Open(); } public override void Run() { base.context.LiftState = Context.runningState; base.context.LiftState.Run(); } public override void Stop() { Console.WriteLine("Stopping"); } }
經過上面的例子能夠直觀得看到狀態模式的特色,它的核心是封裝,狀態的變動引發了行爲的變動,從外部看起來就好像這個對象對應的類發生了改變同樣。
GOF對狀態模式的描述爲:
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
— Design Patterns : Elements of Reusable Object-Oriented Software設計
狀態模式的UML類圖爲
code
狀態模式中有3個角色:對象
public abstract class State { protected Context context; public void SetState(Context context) { this.context = context; } public abstract void Handle1(); public abstract void Handle2(); } public class ConcreteState1 : State { public override void Handle1() { //本狀態下必須處理的邏輯 } public override void Handle2() { base.context.CurrentState = Context.STATE2; base.context.Handle2(); } } public class ConcreteState2 : State { public override void Handle1() { base.context.CurrentState = Context.STATE1; base.context.Handle1(); } public override void Handle2() { //本狀態下必須處理的邏輯 } } public class Context { public readonly static State STATE1 = new ConcreteState1(); public readonly static State STATE2 = new ConcreteState2(); private State currentState; public State CurrentState { get { return currentState; } set { this.currentState = value; this.currentState.SetState(this); } } public void Handle1() { this.CurrentState.Handle1(); } public void Handle2() { this.CurrentState.Handle2(); } }
關於Context類,一般的作法是把狀態對象聲明爲靜態常量,有幾個狀態對象就聲明幾個靜態常量。並且環境角色具備狀態抽象角色定義的全部行爲,具體執行使用委託方式。blog
調用端代碼:接口
public class Test { public static void Entry() { Context context = new Context(); context.CurrentState = Context.STATE1; context.Handle1(); context.Handle2(); } }
優勢get
缺點
狀態模式主要的缺點在於,隨着狀態的增長,子類會變得太多。
參考書籍: 王翔著 《設計模式——基於C#的工程化實現及擴展》