iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
Software Development

深入淺出設計模式 - 使用 C++系列 第 16

[Day 16] 讓物件仿佛變成另一個類別 — 狀態模式 (State Pattern)

  • 分享至 

  • xImage
  •  

定義

狀態模式 (State Pattern) 是一種用於實現狀態機的物件導向方法。主要目的是將一個物件的多種狀態封裝在不同的類別中,從而使狀態轉換更為靈活和可維護

  • https://ithelp.ithome.com.tw/upload/images/20230929/201386437bIYZSB7iB.png

組成

Context (上下文): 保留狀態並代表客戶端與之互動的物件
State (狀態): 定義一個介面來封裝與 Context 物件的某一狀態相關的行為
ConcreteState (具體狀態): 實現 State 介面,代表 Context 的某一具體狀態

  • https://ithelp.ithome.com.tw/upload/images/20230929/20138643ZCysyK8OKw.png

分析

優點

  • 單一職責原則 (SRP)
    • 每個狀態類別只處理一個狀態的行為
  • 開放/封閉原則 (OCP)
    • 新增新的狀態不需要修改既有代碼
  • 易於維護和擴展
    • 分解成多個狀態類別使得代碼更容易理解和維護
  • 減少條件語句
    • 由於每個狀態都是一個類別,減少了使用多個 if 或 switch 語句
  • 更好的可測試性
    • 單獨的狀態類別易於撰寫單元測試

缺點

  • 增加類別數量
    • 對於每個狀態都需要一個獨立的類別,這可能會導致代碼複雜
  • 記憶體和性能開銷
    • 需要為每個狀態創建對象,這可能會消耗更多的記憶體和CPU資源
  • 初始設計複雜性
    • 設計和實現狀態模式可能相對複雜
  • 過度設計風險
    • 對於簡單的狀態機,使用狀態模式可能會過度設計

應用場景

  • 工作流管理系統: 可以用來表示一個工作項目或任務的不同階段,例如待批准、審核中、已完成等
  • 線上訂單處理: 一個訂單可能有多個狀態,如待支付、已支付、運輸中、已到達等。狀態設計模式能夠封裝每個狀態相對應的邏輯,例如通知客戶、更新庫存、計算運費等。這樣一來,當訂單的狀態改變時,只需要更改 Context 中的 State 物件即可
  • 遊戲開發: 角色或遊戲物件的多種狀態(例如: 待機、移動、攻擊)
  • 通信協定: 封裝不同的通信狀態和轉換條件,如TCP協定的建立和終止過程
  • 文本編輯器: 不同模式下(插入模式、選擇模式等)的行為
  • 自動駕駛車輛: 不同駕駛模式(例如: 自動駕駛、手動控制)
  • 動態系統控制: 如機器人、工業製程控制等場合,需要在多個狀態之間靈活切換
  • 用戶界面交互: 不同用戶角色或權限等級對應不同的操作界面和功能
  • 資源管理器: 不同的資源狀態(例如: 閒置、使用中、錯誤)
  • 音/視頻播放器: 播放、暫停、停止等多種播放狀態
  • 網絡監控: 網絡設備或連接的多種狀態(例如: 正常、異常、維護模式)
  • 金融交易系統: 交易的多個狀態(例如: 未確認、確認、執行中、已完成)

[補充] 輔助圖表與工具

我們可以用以下輔助圖表來幫助我們更好的設計與簡化 States 及 Actions 之間的關係:

  • 序列圖 (Sequence Diagram)
    • 描繪物件(或狀態)間的消息或事件順序
  • 活動圖 (Activity Diagram)
    • 用於顯示從一狀態到另一狀態的動作和條件
  • 激勵表 (Excitation Table)
    • 對於順序邏輯或狀態機有用,它顯示當前狀態和下一狀態之間的關係,以及觸發這些變化所需的條件,如果您需要一個表格式的表示形式來簡單描述狀態間的轉換和觸發條件,則激勵表是一個好選擇
  • 卡諾圖 (Karnaugh Map)
    • 可以用於可視化狀態間的條件和轉換,特別適用於具有多個輸入/輸出條件的狀態機,卡諾圖也可用於找出簡化的

範例

// 定義狀態介面
class State {
public:
    virtual ~State() {}
    virtual void review() = 0;
    virtual void publish() = 0;
};

// 實作 Draft 狀態
class Draft : public State {
public:
    void review() override {
        std::cout << "Reviewing the draft document.\n";
    }
    void publish() override {
        std::cout << "Draft document cannot be published.\n";
    }
};

// 實作 Moderation 狀態
class Moderation : public State {
public:
    void review() override {
        std::cout << "Document is under moderation. Review again.\n";
    }
    void publish() override {
        std::cout << "Moderated document is now published.\n";
    }
};

// 實作 Publish 狀態
class Publish : public State {
public:
    void review() override {
        std::cout << "Published document cannot be reviewed.\n";
    }
    void publish() override {
        std::cout << "Document is already published.\n";
    }
};


// 定義 Document 類別,其中包含狀態
class Document {
private:
    State* state; // 當前狀態
public:
    Document(State* initialState) : state(initialState) {}

    // 設定新的狀態
    void setState(State* newState) {
        delete state;
        state = newState;
    }

    // 執行 review 行為
    void review() {
        state->review();
    }

    // 執行 publish 行為
    void publish() {
        state->publish();
    }

    ~Document() {
        delete state;
    }
};

int main() {
    // 初始化狀態和 Document
    State* draft = new Draft();
    Document doc(draft);

    // 執行行為
    std::cout << "Current state: Draft\n";
    doc.review();
    doc.publish();

    // 改變狀態到 Moderation
    std::cout << "\nChanging state to: Moderation\n";
    doc.setState(new Moderation());

    // 執行行為
    doc.review();
    doc.publish();

    // 改變狀態到 Publish
    std::cout << "\nChanging state to: Publish\n";
    doc.setState(new Publish());

    // 執行行為
    doc.review();
    doc.publish();
}

Reference

  1. https://refactoring.guru/design-patterns/state
  2. https://ianjustin39.github.io/ianlife/design-pattern/state-pattern/
  3. http://yhhuang1966.blogspot.com/2019/06/finite-state-machine.html

上一篇
[Day 15] 建立樹狀結構的物件 — 組合模式 (Composite Pattern)
下一篇
[Day 17] 控制與物件的接觸 — 代理模式 (Proxy Pattern)
系列文
深入淺出設計模式 - 使用 C++37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言