當對象行為隨內部狀態頻繁變化,且狀態轉移邏輯複雜時,若在類中用大量 if/else 或 switch 判斷狀態,會導致代碼膨脹、難維護、違反開閉原則。狀態模式將每種狀態封裝為獨立類,委託給當前狀態對象處理行為,實現「狀態即類、轉移即換對象」。
State* currentState,提供 request() 委託調用,setState(State*) 切換狀態。virtual void handle(Context*) = 0。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;
}
handle 中,或由 Context 根據條件調用 setState。setState 負責 delete 舊狀態,或使用 unique_ptr<State> 自動管理。currentState。| 優點 | 缺點 |
|---|---|
| 消除巨大條件分支 | 增加類數量(每狀態一類) |
| 狀態轉移顯式化、可視化 | 轉移邏輯分散在多個類中 |
| 易於添加新狀態、新行為 | 過度使用導致碎片化 |
| 符合單一職責、開閉原則 |
狀態模式以「換類換行為」替換「判斷狀態執行代碼」,將狀態相關代碼聚合、隔離,是處理複雜狀態機的利器。C++ 實現時善用多態、智能指針、RAII,能編寫出優雅、安全、可擴展的狀態驅動系統。