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

設計模式實戰:狀態模式在 HTML 解析器中的應用心得

     編輯:關於C語言

狀態模式是行為型設計模式之一,允許物件在內部狀態改變時改變其行為。本文透過實作一個 HTML 解析器,分享狀態模式如何優雅地解決複雜狀態轉換問題,並探討其優缺點與實作細節。

問題背景:HTML 解析的狀態複雜性

HTML 解析本質上是字元流的狀態機處理。字元類型包含:`<`、`>`、`/`、`=`、字母、數字、底線、漢字等。解析過程需識別標籤名、屬性名、屬性值、標籤值等狀態,且狀態間轉換頻繁。

若使用巢狀 switch-case 處理(外層判斷字元、內層判斷當前狀態),程式碼會極度膨脹且難以維護:

do {
    char ch = *itChar;
    switch (ch) {
        case '<':
            switch (state) {
                case START_STATE: /* 處理開始標籤 */ break;
                case TAG_VALUE_STATE: /* 處理標籤值 */ break;
                // ... 更多狀態
            }
            break;
        case '>':
            switch (state) {
                case LEFT_TAG_STATE: /* 處理左標籤 */ break;
                case RIGHT_TAG_STATE: /* 處理右標籤 */ break;
            }
            break;
        // ... 更多字元判斷
    }
} while (!end);

狀態模式重構:將狀態封裝為類別

狀態模式核心思想:為每個狀態建立獨立類別,將狀態相關行為封裝其中,透過 Context 切換狀態物件。

核心類別結構

狀態模式類別圖

  • Context:維護當前狀態指標(State*),提供 Request() 觸發處理,並允許狀態物件呼叫 ChangeState() 切換狀態。
  • State (抽象基類):定義 Handle(Context*) 純虛函數,並提供 ChangeState() 協助切換。
  • ConcreteStateA/B...:具體狀態實作,在 Handle() 中執行該狀態邏輯,並在需要時呼叫 ChangeState() 切換至下一狀態。

關鍵程式碼實作

// State.h
class Context; // 前置宣告
class State {
public:
    virtual ~State() {}
    virtual void Handle(Context* con) = 0;
protected:
    bool ChangeState(Context* con, State* st);
};

class ConcreteStateA : public State {
public:
    void Handle(Context* con) override {
        cout << "處理狀態 A" << endl;
        ChangeState(con, new ConcreteStateB()); // 切換到狀態 B
    }
};

class ConcreteStateB : public State {
public:
    void Handle(Context* con) override {
        cout << "處理狀態 B" << endl;
        ChangeState(con, new ConcreteStateA()); // 切換回狀態 A
    }
};

// Context.h
class Context {
public:
    Context(State* state = nullptr) : _state(state) {}
    ~Context() { delete _state; }
    void Request() { if (_state) _state->Handle(this); }
    bool ChangeState(State* state) {
        delete _state; // 釋放舊狀態
        _state = state;
        return true;
    }
private:
    State* _state;
    friend class State; // 允許 State 存取私有 ChangeState
};

進階議題:狀態物件管理與 Entry/Exit 動作

實務上需考慮:

  1. 物件生命週期管理:建議使用單例模式或工廠模式集中管理狀態物件,避免重複建立/釋放開銷。
  2. Entry/Exit 動作:進入/離開狀態時常需執行初始化/清理。三種實作策略:
    • 在 Request() 前手動呼叫 Entry(),並在 ChangeState() 中呼叫 Exit()。
    • 單例模式下,建構子呼叫 Entry(),解構子呼叫 Exit()(注意:下一狀態 Entry 會在上一狀態 Exit 前執行)。
    • 推薦:工廠模式 + ID 切換。ChangeState(stateID) 內先 delete 舊狀態(觸發 Exit),再 new 新狀態(觸發 Entry),順序正確且記憶體安全。

總結與心得

狀態模式將「狀態判斷邏輯」分散到各具體狀態類別中,消除了龐大的 switch-case,符合開放封閉原則(新增狀態只需新增類別)。但也帶來類別數量增加的代價。適用場景:

  • 物件行為隨狀態劇烈改變
  • 狀態轉換條件複雜
  • 需避免大量條件判斷

HTML 解析器僅是狀態模式的應用例之一。實務中常結合其他模式(如工廠、單例、策略)協同運作,提升程式碼品質與可維護性。

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