什么是狀態模式?
狀態模式是一種行為型設計模式,它允許一個對象在其內部狀態發生改變時,動態改變其行為。通過將狀態相關的邏輯封裝到獨立的類中,狀態模式能夠將狀態管理與行為解耦,從而讓系統更加靈活和可維護。
通俗理解
想象一個自動售貨機,它有以下幾種狀態:
- 無硬幣狀態:等待用戶投入硬幣。
- 有硬幣狀態:用戶可以選擇商品。
- 售貨狀態:用戶選擇商品后,售貨機正在出貨。
- 缺貨狀態:商品已售罄。
售貨機的行為完全依賴于當前的狀態,比如:
- 在無硬幣狀態下,用戶無法選擇商品。
- 在有硬幣狀態下,用戶可以選擇商品。
- 在售貨狀態下,售貨機執行出貨操作。
狀態模式的核心就是:將狀態邏輯抽象為獨立的狀態類,并通過上下文類(如售貨機)動態切換狀態對象,進而改變對象的行為。
狀態模式的特點
優點
-
清晰封裝狀態邏輯:
- 每種狀態的邏輯被集中到對應的狀態類中,邏輯清晰且易于管理。
-
動態切換行為:
- 對象的行為可以通過切換狀態動態改變,無需修改上下文類的代碼。
-
擴展性強:
- 新增狀態只需添加新的狀態類,不影響現有代碼,符合開閉原則。
缺點
-
類的數量增加:
- 每種狀態都需要一個單獨的類,可能導致類數量較多。
-
狀態切換邏輯分散:
- 狀態之間的切換邏輯分布在多個狀態類中,增加了維護的復雜性。
狀態模式的結構
UML類圖
+---------------------+| Context | // 上下文類,管理當前狀態+---------------------+| - state: State || + setState(state) || + request() |+---------------------+^|+---------------------+| State | // 抽象狀態類+---------------------+| + handle() |+---------------------+^|+----------------------+ +----------------------+| ConcreteStateA | | ConcreteStateB | // 具體狀態類+----------------------+ +----------------------+| + handle() | | + handle() |+----------------------+ +----------------------+
組成部分
-
State
(抽象狀態類)- 定義了所有狀態的通用接口,例如
insertCoin()
、selectProduct()
等。
- 定義了所有狀態的通用接口,例如
-
ConcreteState
(具體狀態類)- 實現
State
接口,定義與特定狀態相關的行為。
- 實現
-
Context
(上下文類)- 持有一個狀態對象,調用當前狀態的行為。
- 負責在運行時切換狀態對象。
案例:自動售貨機
需求描述
設計一個簡單的自動售貨機,支持以下功能:
- 無硬幣狀態:用戶不能選擇商品。
- 有硬幣狀態:用戶可以選擇商品。
- 售貨狀態:用戶選擇商品后,售貨機出貨。
目標
- 實現售貨機的狀態管理,使得行為隨著狀態變化而動態改變。
- 狀態切換應簡單且易于擴展。
完整代碼實現
以下是狀態模式在自動售貨機中的應用,輸出為中文,附帶詳細注釋。
#include <iostream>
#include <memory>
#include <string>// 抽象狀態類
class State {
public:virtual void insertCoin() = 0; // 投入硬幣virtual void selectProduct() = 0; // 選擇商品virtual void dispenseProduct() = 0; // 出貨virtual ~State() = default;
};// 前向聲明:解決狀態類互相引用的問題
class VendingMachine;
class HasCoinState;// 上下文類:自動售貨機
class VendingMachine {
private:std::shared_ptr<State> currentState; // 當前狀態public:void setState(std::shared_ptr<State> state) {currentState = state; // 切換狀態}// 調用當前狀態的方法void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};// 具體狀態類:無硬幣狀態
class NoCoinState : public State {
private:VendingMachine* machine; // 上下文public:explicit NoCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬幣已投入,進入有硬幣狀態。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine))); // 切換到有硬幣狀態}void selectProduct() override {std::cout << "請先投入硬幣。" << std::endl;}void dispenseProduct() override {std::cout << "請先投入硬幣并選擇商品。" << std::endl;}
};// 具體狀態類:有硬幣狀態
class HasCoinState : public State {
private:VendingMachine* machine;public:explicit HasCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬幣已存在,請選擇商品。" << std::endl;}void selectProduct() override {std::cout << "商品已選擇,正在出貨。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine))); // 模擬出貨后切換到無硬幣狀態}void dispenseProduct() override {std::cout << "請先選擇商品。" << std::endl;}
};// 客戶端代碼
int main() {// 創建自動售貨機并設置初始狀態VendingMachine machine;machine.setState(std::make_shared<NoCoinState>(&machine));// 測試售貨機的功能std::cout << "=== 測試場景 1:無硬幣狀態 ===" << std::endl;machine.selectProduct(); // 未投硬幣時選擇商品machine.insertCoin(); // 投入硬幣std::cout << "\n=== 測試場景 2:有硬幣狀態 ===" << std::endl;machine.selectProduct(); // 投幣后選擇商品return 0;
}
運行結果
=== 測試場景 1:無硬幣狀態 ===
請先投入硬幣。
硬幣已投入,進入有硬幣狀態。=== 測試場景 2:有硬幣狀態 ===
商品已選擇,正在出貨。
代碼解讀
1. 抽象狀態類(State
)
class State {
public:virtual void insertCoin() = 0;virtual void selectProduct() = 0;virtual void dispenseProduct() = 0;virtual ~State() = default;
};
- 定義了所有狀態的接口。
- 子類實現這些方法來處理具體的狀態邏輯。
2. 上下文類(VendingMachine
)
class VendingMachine {
private:std::shared_ptr<State> currentState;public:void setState(std::shared_ptr<State> state) {currentState = state;}void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};
- 持有當前狀態對象,并將行為委托給當前狀態。
- 通過
setState
方法切換狀態。
3. 具體狀態類
- 無硬幣狀態:
void insertCoin() override {std::cout << "硬幣已投入,進入有硬幣狀態。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine)));
}
- 有硬幣狀態:
void selectProduct() override {std::cout << "商品已選擇,正在出貨。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine)));
}
每個具體狀態實現了當前狀態的行為邏輯,同時可以切換到其他狀態。
狀態模式的應用場景
-
對象行為取決于狀態:
- 自動售貨機、訂單狀態管理、游戲角色狀態(如站立、跑動、跳躍等)。
-
需要消除復雜條件判斷:
- 替代
if-else
或switch
語句,將狀態邏輯分散到獨立的狀態類中。
- 替代
總結
狀態模式是一種非常實用的設計模式,它通過將狀態邏輯封裝到獨立的類中,動態改變對象的行為,從而提高代碼的擴展性和可維護性。
核心優勢
- 行為動態改變:通過切換狀態對象,動態改變對象的行為。
- 易擴展:新增狀態只需添加狀態類,不影響現有代碼。
- 邏輯清晰:每個狀態的邏輯集中在對應的狀態類中,代碼更易于理解和維護。
典型應用
- 自動售貨機
- 在線訂單狀態(待支付、已支付、已發貨等)
- 游戲角色狀態(站立、奔跑、跳躍等)