詳細介紹:設計模式(C++)詳解——狀態模式(State)(2)
<摘要>
狀態模式是一種行為設計模式,允許對象在內部狀態改變時改變其行為,使對象看起來像是修改了它的類。本文從背景概念、設計意圖、應用場景等多個維度深度解析狀態模式,提供完整的C++代碼示例、UML圖表和Makefile,幫助讀者全面理解該模式的實際應用價值。
<解析>
狀態模式深度解析:讓對象"七十二變"的魔法
1. 背景與核心概念
1.1 設計模式的"狀態"演進
想象一下,你有一個智能燈泡,它有多種狀態:關閉、常亮、閃爍、呼吸燈效果等。如果用傳統的if-else來處理所有狀態轉換,代碼會變得多么復雜!這就是狀態模式要解決的問題。
狀態模式誕生于1994年,由"四人幫"(GoF)在《設計模式:可復用面向對象軟件的基礎》中首次提出。它屬于行為型模式家族,專門處理對象狀態相關的行為變化。
1.2 核心概念解析
關鍵術語:
- Context(上下文):維護一個ConcreteState子類的實例,定義當前狀態
- State(狀態):定義一個接口,用于封裝與Context特定狀態相關的行為
- ConcreteState(具體狀態):實現狀態接口,每個子類實現一個與Context狀態相關的行為
讓我們用UML類圖來直觀理解:
1.3 狀態模式 vs 傳統條件語句
傳統方式的痛點:
// 傳統的if-else方式 - 難以維護!
void processRequest() {
if (state == STATE_A) {
// 處理狀態A的邏輯
if (some_condition) {
state = STATE_B;
}
} else if (state == STATE_B) {
// 處理狀態B的邏輯
if (another_condition) {
state = STATE_C;
}
} else if (state == STATE_C) {
// ... 更多條件判斷
}
}
狀態模式的優雅:
// 狀態模式 - 每個狀態獨立管理
void Context::request() {
state->handle(this); // 委托給當前狀態處理
}
2. 設計意圖與考量
2.1 核心設計目標
狀態模式的設計哲學可以用一句話概括:“將狀態相關的行為局部化,并且將不同狀態的行為分割開來”。
主要目標:
- 消除龐大的條件分支語句
- 使狀態轉換顯式化
- 提高代碼的可維護性和擴展性
- 符合開閉原則(對擴展開放,對修改關閉)
2.2 設計權衡因素
優點:
- ? 單一職責原則:每個狀態類只負責自己的行為
- ? 開閉原則:新增狀態無需修改現有代碼
- ? 使狀態轉換更加明確
- ? 簡化上下文類的邏輯
缺點:
- ? 如果狀態很少或很少變化,可能過度設計
- ? 狀態類數量可能較多
- ? 狀態轉換邏輯可能分散在各個狀態類中
2.3 狀態轉換的兩種策略
1. 由Context負責狀態轉換:
// Context控制狀態轉換
void Context::changeState(State* newState) {
delete state; // 清理舊狀態
state = newState;
}
2. 由State子類負責狀態轉換:
// 狀態類自己決定下一個狀態
void ConcreteStateA::handle(Context* context) {
// 處理邏輯...
context->setState(new ConcreteStateB());
}
3. 實例與應用場景
3.1 案例一:智能交通燈控制系統
場景描述:
城市交通燈有紅、黃、綠三種狀態,每種狀態有不同的持續時間和行為規則。
完整代碼實現:
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <map>
/**
* @brief 交通燈狀態接口
*
* 定義交通燈狀態的基本行為,包括顯示當前狀態和切換到下一個狀態。
*/
class TrafficLightState {
public:
virtual ~TrafficLightState() = default;
/**
* @brief 顯示當前交通燈狀態
*
* @in:
* - duration: 當前狀態的持續時間(秒)
*
* @return:
* 無返回值
*/
virtual void display(int duration) = 0;
/**
* @brief 獲取下一個狀態
*
* @return:
* 返回下一個狀態的智能指針
*/
virtual std::unique_ptr<TrafficLightState> nextState() = 0;
/**
* @brief 獲取狀態名稱
*
* @return:
* 返回狀態名稱字符串
*/
virtual std::string getName() = 0;
/**
* @brief 獲取狀態持續時間
*
* @return:
* 返回狀態的持續時間(秒)
*/
virtual int getDuration() = 0;
};
/**
* @brief 紅燈狀態
*
* 表示交通燈的紅燈狀態,持續30秒。
*/
class RedLightState : public TrafficLightState {
public:
void display(int duration) override {
std::cout << " 紅燈 - 禁止通行 | 剩余時間: " << duration << "秒" << std::endl;
}
std::unique_ptr<TrafficLightState> nextState() override {
return std::make_unique<GreenLightState>();
}
std::string getName() override {
return "紅燈";
}
int getDuration() override {
return 30; // 紅燈持續30秒
}
};
/**
* @brief 綠燈狀態
*
* 表示交通燈的綠燈狀態,持續40秒。
*/
class GreenLightState : public TrafficLightState {
public:
void display(int duration) override {
std::cout << " 綠燈 - 允許通行 | 剩余時間: " << duration << "秒" << std::endl;
}
std::unique_ptr<TrafficLightState> nextState() override {
return std::make_unique<YellowLightState>();
}
std::string getName() override {
return "綠燈";
}
int getDuration() override {
return 40; // 綠燈持續40秒
}
};
/**
* @brief 黃燈狀態
*
* 表示交通燈的黃燈狀態,持續5秒。
*/
class YellowLightState : public TrafficLightState {
public:
void display(int duration) override {
std::cout << " 黃燈 - 準備停止 | 剩余時間: " << duration << "秒" << std::endl;
}
std::unique_ptr<TrafficLightState> nextState() override {
return std::make_unique<RedLightState>();
}
std::string getName() override {
return "黃燈";
}
int getDuration() override {
return 5; // 黃燈持續5秒
}
};
/**
* @brief 交通燈上下文類
*
* 維護當前交通燈狀態,并提供狀態切換和顯示功能。
*/
class TrafficLight {
private:
std::unique_ptr<TrafficLightState> currentState;
int remainingTime;
public:
/**
* @brief 構造函數,初始化為紅燈狀態
*/
TrafficLight() : currentState(std::make_unique<RedLightState>()),
remainingTime(currentState->getDuration()) {}
/**
* @brief 顯示當前交通燈狀態
*
* 顯示當前狀態信息并更新剩余時間。
*
* @return:
* 無返回值
*/
void display() {
currentState->display(remainingTime);
}
/**
* @brief 切換到下一個狀態
*
* 當剩余時間為0時,自動切換到下一個狀態。
*
* @return:
* 無返回值
*/
void next() {
remainingTime--;
if (remainingTime <= 0) {
auto nextState = currentState->nextState();
std::cout << "\n=== 狀態切換: " << currentState->getName()
<< " → " << nextState->getName() << " ===\n" << std::endl;
currentState = std::move(nextState);
remainingTime = currentState->getDuration();
}
}
/**
* @brief 獲取當前狀態名稱
*
* @return:
* 返回當前狀態名稱
*/
std::string getCurrentStateName() {
return currentState->getName();
}
/**
* @brief 模擬交通燈運行
*
* 運行指定數量的周期,每個周期1秒。
*
* @in:
* - cycles: 要運行的周期數
*
* @return:
* 無返回值
*/
void run(int cycles) {
std::cout << " 交通燈系統啟動!初始狀態: " << getCurrentStateName() << "\n" << std::endl;
for (int i = 0; i < cycles; ++i) {
display();
next();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
};
/**
* @brief 主函數 - 交通燈系統演示
*
* 創建交通燈實例并運行多個周期,展示狀態模式的應用。
*
* @return:
* 程序執行成功返回0
*/
int main() {
std::cout << "==========================================" << std::endl;
std::cout << " 智能交通燈控制系統演示" << std::endl;
std::cout << "==========================================" << std::endl;
TrafficLight trafficLight;
// 運行3個完整周期(紅->綠->黃->紅)
trafficLight.run(80); // 30+40+5=75秒一個周期,運行80秒展示完整轉換
std::cout << "\n==========================================" << std::endl;
std::cout << " 演示結束" << std::endl;
std::cout << "==========================================" << std::endl;
return 0;
}
交通燈狀態轉換流程圖:
3.2 案例二:文檔審批工作流系統
場景描述:
文檔審批流程包含多個狀態:草稿、待審批、已批準、已拒絕、已發布。每個狀態有不同的操作權限和轉換規則。
完整代碼實現:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <map>
// 前向聲明
class DocumentState;
/**
* @brief 文檔上下文類
*
* 維護文檔的當前狀態和內容,提供狀態轉換接口。
*/
class Document {
private:
std::unique_ptr<DocumentState> currentState;
std::string content;
std::string title;
std::vector<std::string> approvalHistory;
public:
Document(const std::string& docTitle, const std::string& docContent);
void setState(std::unique_ptr<DocumentState> newState);
void addApprovalRecord(const std::string& record);
// 文檔操作
void edit(const std::string& newContent);
void submitForApproval();
void approve(const std::string& approver);
void reject(const std::string& reviewer);
void publish();
void displayStatus();
// Getter方法
std::string getContent() const { return content; }
std::string getTitle() const { return title; }
const std::vector<std::string>& getApprovalHistory() const { return approvalHistory; }
void setContent(const std::string& newContent) { content = newContent; }
};
/**
* @brief 文檔狀態接口
*
* 定義文檔狀態的基本操作。
*/
class DocumentState {
public:
virtual ~DocumentState() = default;
virtual void edit(Document* document, const std::string& newContent) = 0;
virtual void submitForApproval(Document* document) = 0;
virtual void approve(Document* document, const std::string& approver) = 0;
virtual void reject(Document* document, const std::string& reviewer) = 0;
virtual void publish(Document* document) = 0;
virtual std::string getName() = 0;
virtual void displayStatus(Document* document) = 0;
};
/**
* @brief 草稿狀態
*
* 文檔初始狀態,允許編輯和提交審批。
*/
class DraftState : public DocumentState {
public:
void edit(Document* document, const std::string& newContent) override;
void submitForApproval(Document* document) override;
void approve(Document* document, const std::string& approver) override;
void reject(Document* document, const std::string& reviewer) override;
void publish(Document* document) override;
std::string getName() override { return "草稿"; }
void displayStatus(Document* document) override;
};
/**
* @brief 待審批狀態
*
* 文檔已提交審批,等待審批結果。
*/
class PendingApprovalState : public DocumentState {
public:
void edit(Document* document, const std::string& newContent) override;
void submitForApproval(Document* document) override;
void approve(Document* document, const std::string& approver) override;
void reject(Document* document, const std::string& reviewer) override;
void publish(Document* document) override;
std::string getName() override { return "待審批"; }
void displayStatus(Document* document) override;
};
/**
* @brief 已批準狀態
*
* 文檔已通過審批,可以發布。
*/
class ApprovedState : public DocumentState {
public:
void edit(Document* document, const std::string& newContent) override;
void submitForApproval(Document* document) override;
void approve(Document* document, const std::string& approver) override;
void reject(Document* document, const std::string& reviewer) override;
void publish(Document* document) override;
std::string getName() override { return "已批準"; }
void displayStatus(Document* document) override;
};
/**
* @brief 已拒絕狀態
*
* 文檔被拒絕,需要修改后重新提交。
*/
class RejectedState : public DocumentState {
public:
void edit(Document* document, const std::string& newContent) override;
void submitForApproval(Document* document) override;
void approve(Document* document, const std::string& approver) override;
void reject(Document* document, const std::string& reviewer) override;
void publish(Document* document) override;
std::string getName() override { return "已拒絕"; }
void displayStatus(Document* document) override;
};
/**
* @brief 已發布狀態
*
* 文檔已發布,不能再修改。
*/
class PublishedState : public DocumentState {
public:
void edit(Document* document, const std::string& newContent) override;
void submitForApproval(Document* document) override;
void approve(Document* document, const std::string& approver) override;
void reject(Document* document, const std::string& reviewer) override;
void publish(Document* document) override;
std::string getName() override { return "已發布"; }
void displayStatus(Document* document) override;
};
// Document 類實現
Document::Document(const std::string& docTitle, const std::string& docContent)
: title(docTitle), content(docContent) {
currentState = std::make_unique<DraftState>();
approvalHistory.push_back("文檔創建 - 初始狀態: 草稿");
}
void Document::setState(std::unique_ptr<DocumentState> newState) {
currentState = std::move(newState);
}
void Document::addApprovalRecord(const std::string& record) {
approvalHistory.push_back(record);
}
void Document::edit(const std::string& newContent) {
currentState->edit(this, newContent);
}
void Document::submitForApproval() {
currentState->submitForApproval(this);
}
void Document::approve(const std::string& approver) {
currentState->approve(this, approver);
}
void Document::reject(const std::string& reviewer) {
currentState->reject(this, reviewer);
}
void Document::publish() {
currentState->publish(this);
}
void Document::displayStatus() {
currentState->displayStatus(this);
}
// DraftState 實現
void DraftState::edit(Document* document, const std::string& newContent) {
document->setContent(newContent);
document->addApprovalRecord("文檔內容已更新");
std::cout << "? 文檔內容已更新" << std::endl;
}
void DraftState::submitForApproval(Document* document) {
document->setState(std::make_unique<PendingApprovalState>());
document->addApprovalRecord("文檔已提交審批");
std::cout << " 文檔已提交審批,等待審批中..." << std::endl;
}
void DraftState::approve(Document* document, const std::string& approver) {
std::cout << "? 錯誤:草稿狀態的文檔不能被批準" << std::endl;
}
void DraftState::reject(Document* document, const std::string& reviewer) {
std::cout << "? 錯誤:草稿狀態的文檔不能被拒絕" << std::endl;
}
void DraftState::publish(Document* document) {
std::cout << "? 錯誤:草稿狀態的文檔不能發布" << std::endl;
}
void DraftState::displayStatus(Document* document) {
std::cout << "\n 文檔狀態: 草稿" << std::endl;
std::cout << " 可執行操作: 編輯、提交審批" << std::endl;
}
// PendingApprovalState 實現
void PendingApprovalState::edit(Document* document, const std::string& newContent) {
std::cout << "? 錯誤:待審批狀態的文檔不能編輯" << std::endl;
}
void PendingApprovalState::submitForApproval(Document* document) {
std::cout << "?? 文檔已在審批隊列中,無需重復提交" << std::endl;
}
void PendingApprovalState::approve(Document* document, const std::string& approver) {
document->setState(std::make_unique<ApprovedState>());
std::string record = "文檔已由 " + approver + " 批準";
document->addApprovalRecord(record);
std::cout << "? " << record << std::endl;
}
void PendingApprovalState::reject(Document* document, const std::string& reviewer) {
document->setState(std::make_unique<RejectedState>());
std::string record = "文檔被 " + reviewer + " 拒絕";
document->addApprovalRecord(record);
std::cout << "? " << record << std::endl;
}
void PendingApprovalState::publish(Document* document) {
std::cout << "? 錯誤:待審批狀態的文檔不能發布" << std::endl;
}
void PendingApprovalState::displayStatus(Document* document) {
std::cout << "\n 文檔狀態: 待審批" << std::endl;
std::cout << " 可執行操作: 批準、拒絕" << std::endl;
}
// ApprovedState 實現
void ApprovedState::edit(Document* document, const std::string& newContent) {
std::cout << "? 錯誤:已批準的文檔不能直接編輯,如需修改請先拒絕" << std::endl;
}
void ApprovedState::submitForApproval(Document* document) {
std::cout << "?? 文檔已批準,無需重新提交" << std::endl;
}
void ApprovedState::approve(Document* document, const std::string& approver) {
std::cout << "?? 文檔已處于批準狀態" << std::endl;
}
void ApprovedState::reject(Document* document, const std::string& reviewer) {
document->setState(std::make_unique<RejectedState>());
std::string record = "文檔被 " + reviewer + " 重新拒絕";
document->addApprovalRecord(record);
std::cout << "? " << record << std::endl;
}
void ApprovedState::publish(Document* document) {
document->setState(std::make_unique<PublishedState>());
document->addApprovalRecord("文檔已發布");
std::cout << " 文檔已成功發布!" << std::endl;
}
void ApprovedState::displayStatus(Document* document) {
std::cout << "\n 文檔狀態: 已批準" << std::endl;
std::cout << " 可執行操作: 發布、拒絕" << std::endl;
}
// RejectedState 實現
void RejectedState::edit(Document* document, const std::string& newContent) {
document->setState(std::make_unique<DraftState>());
document->setContent(newContent);
document->addApprovalRecord("文檔已修改并退回草稿狀態");
std::cout << "? 文檔已修改并退回草稿狀態" << std::endl;
}
void RejectedState::submitForApproval(Document* document) {
document->setState(std::make_unique<PendingApprovalState>());
document->addApprovalRecord("文檔重新提交審批");
std::cout << " 文檔重新提交審批" << std::endl;
}
void RejectedState::approve(Document* document, const std::string& approver) {
std::cout << "? 錯誤:已拒絕的文檔不能被批準,請先重新提交" << std::endl;
}
void RejectedState::reject(Document* document, const std::string& reviewer) {
std::cout << "?? 文檔已處于拒絕狀態" << std::endl;
}
void RejectedState::publish(Document* document) {
std::cout << "? 錯誤:已拒絕的文檔不能發布" << std::endl;
}
void RejectedState::displayStatus(Document* document) {
std::cout << "\n 文檔狀態: 已拒絕" << std::endl;
std::cout << " 可執行操作: 編輯、重新提交" << std::endl;
}
// PublishedState 實現
void PublishedState::edit(Document* document, const std::string& newContent) {
std::cout << "? 錯誤:已發布的文檔不能編輯" << std::endl;
}
void PublishedState::submitForApproval(Document* document) {
std::cout << "? 錯誤:已發布的文檔不能重新提交審批" << std::endl;
}
void PublishedState::approve(Document* document, const std::string& approver) {
std::cout << "?? 文檔已發布,無需批準" << std::endl;
}
void PublishedState::reject(Document* document, const std::string& reviewer) {
std::cout << "? 錯誤:已發布的文檔不能被拒絕" << std::endl;
}
void PublishedState::publish(Document* document) {
std::cout << "?? 文檔已處于發布狀態" << std::endl;
}
void PublishedState::displayStatus(Document* document) {
std::cout << "\n 文檔狀態: 已發布" << std::endl;
std::cout << " 可執行操作: 無(文檔已最終發布)" << std::endl;
}
/**
* @brief 顯示審批歷史
*
* @in:
* - document: 要顯示歷史的文檔對象
*
* @return:
* 無返回值
*/
void displayApprovalHistory(const Document& document) {
std::cout << "\n 審批歷史:" << std::endl;
std::cout << "-------------------" << std::endl;
for (const auto& record : document.getApprovalHistory()) {
std::cout << "? " << record << std::endl;
}
std::cout << "-------------------" << std::endl;
}
/**
* @brief 主函數 - 文檔審批系統演示
*
* 演示完整的文檔審批工作流程。
*
* @return:
* 程序執行成功返回0
*/
int main() {
std::cout << "==========================================" << std::endl;
std::cout << " 文檔審批工作流系統演示" << std::endl;
std::cout << "==========================================" << std::endl;
// 創建新文檔
Document doc("項目計劃書", "這是一個重要的項目計劃文檔...");
std::cout << "\n1. 初始狀態:" << std::endl;
doc.displayStatus();
std::cout << "\n2. 編輯文檔:" << std::endl;
doc.edit("更新后的項目計劃內容...");
std::cout << "\n3. 提交審批:" << std::endl;
doc.submitForApproval();
std::cout << "\n4. 嘗試在待審批狀態編輯(應該失?。?" << std::endl;
doc.edit("非法修改的內容...");
std::cout << "\n5. 批準文檔:" << std::endl;
doc.approve("張經理");
std::cout << "\n6. 發布文檔:" << std::endl;
doc.publish();
std::cout << "\n7. 最終狀態:" << std::endl;
doc.displayStatus();
// 顯示完整審批歷史
displayApprovalHistory(doc);
std::cout << "\n==========================================" << std::endl;
std::cout << " 演示結束" << std::endl;
std::cout << "==========================================" << std::endl;
return 0;
}
文檔審批狀態轉換圖:
4. Makefile范例
# 編譯器設置
CXX := g++
CXXFLAGS := -std=c++14 -Wall -Wextra -O2 -g
LDFLAGS := -lpthread
# 目標文件
TARGETS := traffic_light document_workflow
ALL_OBJS := traffic_light.o document_workflow.o
# 默認目標
all: $(TARGETS)
# 交通燈系統
traffic_light: traffic_light.o
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
traffic_light.o: traffic_light.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# 文檔工作流系統
document_workflow: document_workflow.o
$(CXX) $(CXXFLAGS) -o $@ $^
document_workflow.o: document_workflow.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# 清理
clean:
rm -f $(TARGETS) $(ALL_OBJS)
# 安裝依賴(如果需要)
deps:
@echo "檢查系統依賴..."
@which $(CXX) > /dev/null || (echo "錯誤: 請安裝g++編譯器" && exit 1)
@echo "依賴檢查通過"
# 測試
test: all
@echo "運行交通燈系統測試..."
@./traffic_light || true
@echo ""
@echo "運行文檔工作流系統測試..."
@./document_workflow || true
.PHONY: all clean deps test
5. 案例操作說明
5.1 交通燈系統
編譯方法:
# 檢查依賴
make deps
# 編譯所有目標
make
# 或者單獨編譯交通燈系統
make traffic_light
運行方式:
./traffic_light
結果解讀:
交通燈系統啟動!初始狀態: 紅燈
紅燈 - 禁止通行 | 剩余時間: 30秒
紅燈 - 禁止通行 | 剩余時間: 29秒
...
=== 狀態切換: 紅燈 → 綠燈 ===
綠燈 - 允許通行 | 剩余時間: 40秒
...
5.2 文檔審批系統
編譯方法:
# 編譯所有目標
make
# 或者單獨編譯文檔審批系統
make document_workflow
運行方式:
./document_workflow
結果解讀:
文檔狀態: 草稿
可執行操作: 編輯、提交審批
? 文檔內容已更新
文檔已提交審批,等待審批中...
? 錯誤:待審批狀態的文檔不能編輯
? 文檔已由 張經理 批準
文檔已成功發布!
6. 交互性內容解析
在文檔審批系統中,各個狀態之間的交互通過明確的接口進行。讓我們使用時序圖來展示一個完整的審批流程:
7. 狀態模式的最佳實踐
7.1 何時使用狀態模式
推薦使用場景:
- 對象的行為取決于它的狀態,并且必須在運行時根據狀態改變行為
- 操作中有大量的條件語句,這些條件語句依賴于對象的狀態
- 狀態轉換邏輯復雜,需要清晰的管理
- 需要避免使用大量的條件分支語句
7.2 實現注意事項
狀態對象的生命周期:
- 如果狀態無狀態,可以使用單例模式共享狀態實例
- 如果狀態有狀態,需要為每個上下文創建新的狀態實例
- 注意內存管理,避免內存泄漏
狀態轉換的觸發:
- 可以由狀態類自身觸發(自管理的狀態轉換)
- 可以由上下文類觸發(集中管理的狀態轉換)
- 可以根據外部事件觸發(事件驅動的狀態轉換)
8. 總結
狀態模式是一種強大的設計模式,它通過將不同狀態的行為封裝到獨立的類中,使得代碼更加清晰、可維護和可擴展。就像交通燈系統一樣,每個狀態都知道自己該做什么,以及下一步應該切換到什么狀態。
狀態模式的核心價值:
- 消除條件復雜性:用多態代替復雜的條件判斷
- 提高可維護性:每個狀態的變化只影響一個類
- 增強擴展性:新增狀態只需添加新類,無需修改現有代碼
- 明確狀態轉換:狀態轉換邏輯更加清晰可見
通過本文的兩個完整案例,您可以看到狀態模式在實際項目中的強大應用。無論是基礎設施系統(如交通燈)還是業務系統(如工作流),狀態模式都能讓您的代碼更加優雅和健壯。
記住,好的設計模式就像是編程中的"魔法",它們能讓復雜的邏輯變得簡單,讓混亂的代碼變得有序。狀態模式正是這樣一種讓對象擁有"七十二變"能力的強大魔法!

浙公網安備 33010602011771號