一、為什么要用狀態模式?
在開發中,經常遇到“對象在不同狀態下行為不同”的情況。最常見的寫法是用一堆 if/else
或 switch
來判斷狀態,然后在不同分支里寫邏輯。這樣做有兩個問題:
狀態增多后,條件分支會變得臃腫。
修改或擴展某個狀態邏輯時,可能要在多個地方改代碼。
狀態模式的核心思想是:
把“狀態”抽象成獨立的類,每個狀態只關心自己能做什么。
上下文對象(這里是
NetworkConnection
)只需要把調用轉發給當前狀態。狀態對象在需要時可以切換上下文的狀態。
這樣就能讓分支邏輯“消失”,變成結構清晰的多態調用。
二、場景說明
網絡連接的三個狀態:
Disconnected:允許調用
connect()
,否則提示非法操作。Connecting:連接過程中,
connect()
和disconnect()
都無效,必須等待連接結果。Connected:允許
send()
數據,也可以disconnect()
主動斷開。
三、類圖
四、C++ 實現代碼
這段代碼完整實現了一個簡化的 網絡連接狀態機。
NetworkConnection
作為上下文類,持有當前狀態對象,并對外提供統一的接口(connect
、disconnect
、send
、onConnectResult
)。Disconnected
、Connecting
、Connected
三個狀態類分別封裝了各自的行為邏輯。狀態之間的切換由狀態類自身觸發,例如
Disconnected
調用connect()
會進入Connecting
,而Connecting
根據onConnectResult()
的結果進入Connected
或回到Disconnected
。
通過這種方式,代碼避免了大量的 if/else
判斷,讓狀態邏輯更加清晰,擴展新狀態時也更加方便。
#include <iostream>
#include <memory>
#include <string>class NetworkConnection; // 前向聲明// 狀態接口
struct State {virtual ~State() = default;virtual void connect(NetworkConnection& nc) = 0;virtual void disconnect(NetworkConnection& nc) = 0;virtual void send(NetworkConnection& nc, const std::string& data) = 0;virtual void onConnectResult(NetworkConnection& nc, bool success) = 0;
};// 上下文類
class NetworkConnection {
public:NetworkConnection();void connect() { currentState->connect(*this); }void disconnect() { currentState->disconnect(*this); }void send(const std::string& data) { currentState->send(*this, data); }void onConnectResult(bool success) { currentState->onConnectResult(*this, success); }void setState(std::unique_ptr<State> s) { currentState = std::move(s); }private:std::unique_ptr<State> currentState;
};// 具體狀態:未連接
struct Disconnected : State {void connect(NetworkConnection& nc) override;void disconnect(NetworkConnection& nc) override {std::cout << "[Disconnected] 已經處于未連接狀態。\n";}void send(NetworkConnection& nc, const std::string& data) override {std::cout << "[Disconnected] 無法發送數據,尚未連接。\n";}void onConnectResult(NetworkConnection& nc, bool success) override {std::cout << "[Disconnected] 忽略連接結果(當前未連接)。\n";}
};// 具體狀態:連接中
struct Connecting : State {void connect(NetworkConnection& nc) override {std::cout << "[Connecting] 正在連接中,不能重復連接。\n";}void disconnect(NetworkConnection& nc) override {std::cout << "[Connecting] 連接進行中,不能斷開。\n";}void send(NetworkConnection& nc, const std::string& data) override {std::cout << "[Connecting] 連接尚未建立,無法發送。\n";}void onConnectResult(NetworkConnection& nc, bool success) override;
};// 具體狀態:已連接
struct Connected : State {void connect(NetworkConnection& nc) override {std::cout << "[Connected] 已經處于連接狀態。\n";}void disconnect(NetworkConnection& nc) override {std::cout << "[Connected] 主動斷開連接。\n";nc.setState(std::make_unique<Disconnected>());}void send(NetworkConnection& nc, const std::string& data) override {std::cout << "[Connected] 發送數據:" << data << "\n";}void onConnectResult(NetworkConnection& nc, bool success) override {std::cout << "[Connected] 忽略連接結果(已經連接)。\n";}
};// --- 狀態切換邏輯實現 ---
void Disconnected::connect(NetworkConnection& nc) {std::cout << "[Disconnected] 嘗試連接...\n";nc.setState(std::make_unique<Connecting>());
}void Connecting::onConnectResult(NetworkConnection& nc, bool success) {if (success) {std::cout << "[Connecting] 連接成功,進入已連接狀態。\n";nc.setState(std::make_unique<Connected>());} else {std::cout << "[Connecting] 連接失敗,回到未連接狀態。\n";nc.setState(std::make_unique<Disconnected>());}
}// --- NetworkConnection 構造 ---
NetworkConnection::NetworkConnection() {setState(std::make_unique<Disconnected>());
}// --- 演示程序 ---
int main() {NetworkConnection nc;nc.send("test"); // 未連接 -> 拒絕nc.connect(); // Disconnected -> Connectingnc.connect(); // Connecting -> 拒絕重復nc.onConnectResult(true); // 成功 -> Connectednc.send("hello world"); // 已連接 -> 可發送nc.disconnect(); // -> Disconnectednc.onConnectResult(false);// 忽略(已斷開)return 0;
}
輸出示例
[Disconnected] 無法發送數據,尚未連接。
[Disconnected] 嘗試連接...
[Connecting] 正在連接中,不能重復連接。
[Connecting] 連接成功,進入已連接狀態。
[Connected] 發送數據:hello world
[Connected] 主動斷開連接。
[Disconnected] 忽略連接結果(當前未連接)。
這個示例里用 onConnectResult(bool success)
來模擬網絡異步回調,簡化成一個直接調用的方法,方便演示狀態切換的效果。
在真實項目中,這個函數可能是 事件回調 或 信號槽,并不是手動調用的,而是網絡庫觸發的。
比如在 Qt 中,QTcpSocket::connected()
和 QTcpSocket::errorOccurred()
信號就對應了這種結果通知。
五、總結
通過狀態模式,我們把“未連接 / 連接中 / 已連接”三種狀態的行為邏輯清晰地分散到不同類中,而不是塞在一堆 if/else
里。這樣帶來的好處是:
可讀性高:邏輯一目了然,每個狀態類只關心自己能做什么。
易擴展:如果未來需要加
Reconnecting
或Limited
狀態,只需要新增一個狀態類。符合開閉原則:不需要修改原有狀態類,就能添加新狀態。