首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 狀態模式深度解析:物件行為隨內部狀態動態變化

     編輯:關於C#

前言

當一個物件的行為取決於其內部狀態,且必須在運行時根據狀態改變其行為時,傳統做法是在方法中使用大量 if-else 或 switch 判斷狀態,導致程式碼龐大、難以維護且違反開閉原則。狀態模式將每種狀態相關的行為封裝進獨立的狀態類別中,讓物件將行為委派給當前狀態物件,實現「物件看起來似乎修改了它所屬的類」。本文透過訊息系統根據用戶權限自動切換儲存策略(XML→SQL)完整展示狀態模式實作。

模式結構與角色定義

  • Context(環境角色):Message 類別,維護當前狀態參考 _abstractMessageState,提供 Get()、Insert() 對外介面,內部委派給狀態物件,並在操作前呼叫 StateChangeCheck() 檢查狀態轉換。
  • State(抽象狀態):AbstractMessageState,定義所有狀態共同介面:Get()、Insert()、StateChangeCheck(),並持有 Context 參考以便存取用戶資訊與切換狀態。
  • ConcreteState(具體狀態):XmlMessage(預設狀態)、SqlMessage(管理員狀態),各自實作具體儲存邏輯與狀態轉換條件。
狀態模式類別圖

核心程式碼機制深度解析

1. Context:狀態持有者與委派者

public class Message
{
    private AbstractMessageState _abstractMessageState;
    public AbstractMessageState AbstractMessageState 
    { 
        get { return _abstractMessageState; } 
        set { _abstractMessageState = value; } 
    }

    private string _userId;
    public string UserId { get { return _userId; } set { _userId = value; } }

    public Message(string userId)
    {
        this._userId = userId;
        _abstractMessageState = new XmlMessage(this); // 初始狀態
    }

    public List<MessageModel> Get()
    {
        _abstractMessageState.StateChangeCheck(); // 檢查狀態轉換
        return _abstractMessageState.Get();        // 委派給當前狀態
    }

    public bool Insert(MessageModel mm)
    {
        _abstractMessageState.StateChangeCheck();
        return _abstractMessageState.Insert(mm);
    }
}

關鍵點:

  • 建構子根據初始條件設定初始狀態(此處預設 XmlMessage)。
  • 公開 AbstractMessageState 屬性,允許狀態物件主動切換 Context 的狀態。
  • 每次操作前呼叫 StateChangeCheck(),實現「狀態檢查→行為委派」流程。

2. 抽象狀態:定義協定與持有 Context 參考

public abstract class AbstractMessageState
{
    protected Message _message;
    public Message Message 
    { 
        get { return _message; } 
        set { _message = value; } 
    }

    public abstract void StateChangeCheck();
    public abstract List<MessageModel> Get();
    public abstract bool Insert(MessageModel mm);
}

持有 Context 參考是狀態模式的關鍵:狀態物件可透過它存取 Context 資料(如 UserId)並修改 Context 狀態(_message.AbstractMessageState = new SqlMessage(...))。

3. 具體狀態:封裝行為與轉換邏輯

XmlMessage(預設狀態):檢查若用戶為 admin 則切換至 SqlMessage:

public override void StateChangeCheck()
{
    if (this._message.UserId == "admin")
    {
        this._message.AbstractMessageState = new SqlMessage(this._message);
    }
}

public override List<MessageModel> Get()
{
    var l = new List<MessageModel>();
    l.Add(new MessageModel("XML方式獲取Message", DateTime.Now));
    return l;
}

SqlMessage(管理員狀態):StateChangeCheck() 為空,表示無需轉換(或可加入降級邏輯)。

4. 客戶端測試與狀態轉換驗證

Message m = new Message("admin"); // 建立時為 XmlMessage 狀態
m.Insert(...); // 操作前檢查狀態 → 發現 admin → 切換至 SqlMessage → 執行 SQL 插入
m.Get();       // 已為 SqlMessage 狀態 → 直接執行 SQL 查詢

m = new Message("user"); // 一般用戶
m.Get(); // 維持 XmlMessage 狀態 → 執行 XML 查詢

執行結果分析

True
SQL方式獲取Message 2007-3-6 22:58:17  ← admin 觸發狀態轉換
True
XML方式獲取Message 2007-3-6 22:58:17  ← user 維持原狀態

同一個 Message 物件,因內部狀態(用戶權限)不同,自動呈現完全不同的行為(SQL vs XML),且狀態轉換邏輯封裝在狀態類別內部,Context 完全不知情。

狀態模式 vs 策略模式:核心差異

維度策略模式狀態模式
驅動變化客戶端主動選擇物件內部狀態自動觸發
狀態/策略關係通常獨立、可互換可能有轉換邏輯、生命週期相依
誰持有參考Context 持有 StrategyState 持有 Context(雙向)
切換時機客戶端決定State 內部邏輯決定

優缺點與適用場景

優點

  • 將狀態相關行為局部化,每個狀態類別專注單一職責。
  • 消除大量條件判斷,程式碼更清晰。
  • 狀態轉換邏輯顯式化,易於維護與擴展新狀態。
  • 符合開閉原則:新增狀態只需新增類別,不修改既有程式碼。

缺點

  • 類別數量增加:每個狀態一個類別,轉換邏輯分散在各狀態類別中。
  • 若狀態轉換條件複雜、頻繁變動,維護成本較高。
  • 不適用於「狀態極少且固定」的簡單場景(過度設計)。

經典應用場景

  • 訂單狀態機:新建→付款→發貨→完成→退貨,每狀態不同操作權限與行為。
  • 工作流引擎、遊戲角色狀態(待機、跑動、攻擊、死亡)、TCP 連線狀態。
  • UI 元件:啟用/停用/唯讀模式下的不同互動行為。

總結

狀態模式將「狀態判斷與行為執行」從 Context 中剝離,封裝至獨立狀態類別,實現物件行為隨內部狀態動態變化。本範例以訊息系統為例,展示如何根據用戶權限自動在 XML 與 SQL 儲存策略間切換,完整呈現了 Context、State、ConcreteState 三角色協作與雙向參考機制。掌握狀態模式,能讓你在面對複雜狀態驅動業務邏輯時,設計出結構清晰、易於擴展新狀態與轉換規則的系統架構。


參考資源:DoFactory State Pattern

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved