狀態模式允許一個物件在其內部狀態改變時改變它的行為,用電梯來舉例,電梯可以認為具有開門、關門、運行、停止四種狀態,這四種狀態之間的切換具有多種限制,比如在開門狀態下不電梯不能運行,只能轉為關門狀態;在運行狀態下,電梯只能轉為停止狀態...
設想一下,如果要常規的if-else或者switch-case描述電梯的這幾種狀態間的切換,將生成非常復雜的、邏輯相互交織的代碼,可讀性差且不易維護,
而如果用狀態模式來實作,會是怎樣的呢?
首先創建LiftState,代表抽象的電梯狀態,包含了電梯的四個動作(方法),通過這些方法可以切換到對應的狀態,
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個具體的實作類中自己根據環境來決定如何進行狀態的過渡,
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狀態,其它切換狀態的方法都是空實作,
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類圖為

狀態模式中有3個角色:
- State(抽象狀態角色),介面或抽象類,負責物件狀態定義,并且封裝環境角色以實作狀態切換,
- ConcreteState(具體狀態角色),每一個具體狀態必須完成兩個職責:就是本狀態下要做的事情,以及本狀態如何過渡到其他狀態,
- Context(環境角色),定義客戶端需要的介面,并且負責具體狀態的切換,
狀態模式的通用的代碼
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類,通常的做法是把狀態物件宣告為靜態常量,有幾個狀態物件就宣告幾個靜態常量,而且環境角色具有狀態抽象角色定義的所有行為,具體執行使用委托方式,
呼叫端代碼:
public class Test
{
public static void Entry()
{
Context context = new Context();
context.CurrentState = Context.STATE1;
context.Handle1();
context.Handle2();
}
}
狀態模式的優缺點
優點
- 結構清晰,避免了過多的switch...case或者if...else陳述句的使用,降低了程式的復雜性,提高系統的可維護性,
- 遵循設計原則,很好地體現了開閉原則和單一職責原則,每個狀態都是一個子類,增加狀態就要增加子類,修改狀態則只需要修改對應的子類,
- 封裝性非常好,這也是狀態模式的基本要求,狀態變換放置到類的內部來實作,外部的呼叫不用知道類內部如何實作狀態和行為的變換,
缺點
狀態模式主要的缺點在于,隨著狀態的增加,子類會變得太多,
狀態模式的適用場景
- 行為需要隨狀態的改變而改變時
- 業務邏輯比較復雜,導致程式中大量使用了switch或者if陳述句,為了避免程式結構不清晰,邏輯混亂,可以使用狀態模式來重構,通過擴展子類來實作了條件的判斷處理,
- 另外,使用整體模式也需要注意避免濫用,只有當某個物件在它的狀態發生改變時,它的行為也隨著發生比較大的變化時,才考慮用狀態模式,而且物件的狀態最好不要超過5個,
參考書籍:
王翔著 《設計模式——基于C#的工程化實作及擴展》
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/1068.html
標籤:設計模式
上一篇:行為型模式之中介者模式
下一篇:設計模式-模板方法模式
