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

狀態模式 C++ 實現詳解:封裝狀態轉移與行為變化的設計模式實戰

     編輯:C++ 入門

模式動機

當對象行為隨內部狀態頻繁變化,且狀態轉移邏輯複雜時,若在類中用大量 if/else 或 switch 判斷狀態,會導致代碼膨脹、難維護、違反開閉原則。狀態模式將每種狀態封裝為獨立類,委託給當前狀態對象處理行為,實現「狀態即類、轉移即換對象」。

UML 結構

  • Context:維護 State* currentState,提供 request() 委託調用,setState(State*) 切換狀態。
  • State(抽象基類):定義 virtual void handle(Context*) = 0。
  • ConcreteStateA/B:實現具體行為,內部可調用 context->setState(new StateB()) 觸發轉移。

完整代碼示例:自動販賣機


#include <bits/stdc++.h>
using namespace std;

class VendingMachine; // 前向聲明

// 抽象狀態
class State {
public:
    virtual void insertCoin(VendingMachine* vm) = 0;
    virtual void selectItem(VendingMachine* vm) = 0;
    virtual void dispense(VendingMachine* vm) = 0;
    virtual ~State() = default;
};

// 環境類
class VendingMachine {
    State* currentState;
    int inventory, balance;
public:
    VendingMachine(int inv) : inventory(inv), balance(0) {
        currentState = new NoCoinState();
    }
    void setState(State* s) { delete currentState; currentState = s; }
    void insertCoin() { currentState->insertCoin(this); }
    void selectItem() { currentState->selectItem(this); }
    void dispense() { currentState->dispense(this); }
    // getters for states
    int getInventory() const { return inventory; }
    int getBalance() const { return balance; }
    void addBalance(int v) { balance += v; }
    void reduceInventory() { --inventory; }
    void clearBalance() { balance = 0; }
    bool hasInventory() const { return inventory > 0; }
};

// 具體狀態
class NoCoinState : public State {
public:
    void insertCoin(VendingMachine* vm) override {
        cout << "Coin accepted.\n";
        vm->addBalance(1);
        vm->setState(new HasCoinState());
    }
    void selectItem(VendingMachine* vm) override { cout << "Insert coin first.\n"; }
    void dispense(VendingMachine* vm) override { cout << "Insert coin first.\n"; }
};

class HasCoinState : public State {
public:
    void insertCoin(VendingMachine* vm) override { cout << "Already has coin.\n"; }
    void selectItem(VendingMachine* vm) override {
        if (vm->hasInventory()) {
            cout << "Item selected.\n";
            vm->setState(new SoldState());
        } else {
            cout << "Sold out.\n";
            vm->setState(new SoldOutState());
        }
    }
    void dispense(VendingMachine* vm) override { cout << "Select item first.\n"; }
};

class SoldState : public State {
public:
    void insertCoin(VendingMachine* vm) override { cout << "Wait for dispense.\n"; }
    void selectItem(VendingMachine* vm) override { cout << "Already selected.\n"; }
    void dispense(VendingMachine* vm) override {
        cout << "Dispensing item...\n";
        vm->reduceInventory();
        vm->clearBalance();
        if (vm->hasInventory()) vm->setState(new NoCoinState());
        else vm->setState(new SoldOutState());
    }
};

class SoldOutState : public State {
public:
    void insertCoin(VendingMachine* vm) override { cout << "Machine sold out.\n"; }
    void selectItem(VendingMachine* vm) override { cout << "Machine sold out.\n"; }
    void dispense(VendingMachine* vm) override { cout << "Machine sold out.\n"; }
};

// 測試
int main() {
    VendingMachine vm(2);
    vm.insertCoin(); vm.selectItem(); vm.dispense(); // 正常購買
    vm.insertCoin(); vm.selectItem(); vm.dispense(); // 售罄
    vm.insertCoin(); // 售罄狀態拒絕
    return 0;
}

關鍵設計要點

  1. 狀態對象無狀態化:狀態類不存儲數據,所有數據在 Context 中,狀態對象可共享(單例/飛量)。
  2. 轉移邏輯集中:轉移條件寫在具體狀態的 handle 中,或由 Context 根據條件調用 setState。
  3. 內存管理:setState 負責 delete 舊狀態,或使用 unique_ptr<State> 自動管理。
  4. 線程安全:若 Context 多線程訪問,需鎖保護 currentState。

優缺點權衡

優點缺點
消除巨大條件分支增加類數量(每狀態一類)
狀態轉移顯式化、可視化轉移邏輯分散在多個類中
易於添加新狀態、新行為過度使用導致碎片化
符合單一職責、開閉原則

適用場景

  • 對象行為強依賴狀態,且狀態數有限(<10)
  • 狀態轉移頻繁、邏輯複雜
  • 需動態增減狀態(插件化架構)

總結

狀態模式以「換類換行為」替換「判斷狀態執行代碼」,將狀態相關代碼聚合、隔離,是處理複雜狀態機的利器。C++ 實現時善用多態、智能指針、RAII,能編寫出優雅、安全、可擴展的狀態驅動系統。

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