设计模式(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. 总结
状态模式是一种强大的设计模式,它通过将不同状态的行为封装到独立的类中,使得代码更加清晰、可维护和可扩展。就像交通灯系统一样,每个状态都知道自己该做什么,以及下一步应该切换到什么状态。
状态模式的核心价值:
- 消除条件复杂性:用多态代替复杂的条件判断
- 提高可维护性:每个状态的变化只影响一个类
- 增强扩展性:新增状态只需添加新类,无需修改现有代码
- 明确状态转换:状态转换逻辑更加清晰可见
通过本文的两个完整案例,您可以看到状态模式在实际项目中的强大应用。无论是基础设施系统(如交通灯)还是业务系统(如工作流),状态模式都能让您的代码更加优雅和健壮。
记住,好的设计模式就像是编程中的"魔法",它们能让复杂的逻辑变得简单,让混乱的代码变得有序。状态模式正是这样一种让对象拥有"七十二变"能力的强大魔法!