概述

行为型模式关注对象之间的通信方式——谁调用谁、数据怎么流动、责任怎么分配。这一部分的 9 个组件,每个都把一种行为模式封装成可以直接 #include 就用的头文件。

核心思路: 不是教模式本身,而是展示如何用 C++ 现代特性(std::variant、CRTP、Policy-Based Design、类型擦除)把这些模式变成零侵入的组件。


1. Command 组件 — 可撤销命令框架

意图与痛点

你有一个编辑器、一个操作队列、或者任何需要"撤销/重做"的场景。传统做法是给每个操作写一对 do()undo(),散落在各处,难以管理和序列化。

实现结构

graph TD
    COM["<b>Command</b><br/>+execute() void<br/>+undo() void<br/>+description() string"]
    COM["<b>CommandHistory</b><br/>-stack~Command*~ doneStack<br/>-stack~Command*~ undoneStack<br/>+execute(cmd) void<br/>+undo() void<br/>+redo() void<br/>+clear() void"]
    MAC["<b>MacroCommand</b><br/>-vector~Command*~ commands<br/>+add(cmd) void<br/>+execute() void<br/>+undo() void"]
    COM -.-> MAC|-.-|
    MAC -.-> COM|-.-|
    COM --> COM

核心代码

// command.hpp
#pragma once
#include <memory>
#include <stack>
#include <string>
#include <vector>

namespace design_patterns::command {

// 命令基类 — 只需要实现三个方法
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
    virtual std::string description() const { return "unnamed command"; }
};

// 命令历史 — 栈式 undo/redo
class CommandHistory {
public:
    void execute(std::unique_ptr<Command> cmd) {
        cmd->execute();
        doneStack_.push(std::move(cmd));
        // 新命令到来,清空 redo 栈
        while (!undoneStack_.empty()) undoneStack_.pop();
    }

    bool canUndo() const { return !doneStack_.empty(); }
    bool canRedo() const { return !undoneStack_.empty(); }

    void undo() {
        if (!canUndo()) return;
        auto cmd = std::move(doneStack_.top());
        doneStack_.pop();
        cmd->undo();
        undoneStack_.push(std::move(cmd));
    }

    void redo() {
        if (!canRedo()) return;
        auto cmd = std::move(undoneStack_.top());
        undoneStack_.pop();
        cmd->execute();
        doneStack_.push(std::move(cmd));
    }

    void clear() {
        while (!doneStack_.empty()) doneStack_.pop();
        while (!undoneStack_.empty()) undoneStack_.pop();
    }

private:
    std::stack<std::unique_ptr<Command>> doneStack_;
    std::stack<std::unique_ptr<Command>> undoneStack_;
};

// 宏命令 — 把多个命令打包成一个
class MacroCommand : public Command {
public:
    void add(std::unique_ptr<Command> cmd) {
        commands_.push_back(std::move(cmd));
    }

    void execute() override {
        for (auto& cmd : commands_) cmd->execute();
    }

    void undo() override {
        // 反向撤销
        for (auto it = commands_.rbegin(); it != commands_.rend(); ++it)
            (*it)->undo();
    }

private:
    std::vector<std::unique_ptr<Command>> commands_;
};

} // namespace design_patterns::command

即插即用

#include "command.hpp"
using namespace design_patterns::command;

// 只需继承 Command,实现 execute() 和 undo()
class AppendTextCmd : public Command {
    std::string& target_;
    std::string text_;
public:
    AppendTextCmd(std::string& t, std::string text) : target_(t), text_(std::move(text)) {}
    void execute() override { target_ += text_; }
    void undo() override { target_.resize(target_.size() - text_.size()); }
};

使用示例

CommandHistory history;
std::string document;

history.execute(std::make_unique<AppendTextCmd>(document, "Hello "));
history.execute(std::make_unique<AppendTextCmd>(document, "World!"));
history.undo();  // document == "Hello "
history.redo();  // document == "Hello World!"

进阶扩展

  • 命令序列化:Commandserialize()/deserialize(),存到文件实现持久化
  • 事务命令: execute() 失败时自动回滚
  • 异步命令: execute() 返回 std::future<void>

常见陷阱

陷阱 解决
undo 时引用失效 shared_ptr 共享数据,或存储值拷贝
宏命令中某个子命令失败 用事务模式,全部成功或全部回滚
命令对象膨胀 用 lambda 替代小命令:std::function<void()>

2. Strategy 组件 — Policy-Based Design

意图与痛点

同一个操作有多种算法实现——排序可以用快排/归并/堆排,日志可以写文件/发网络/打控制台。传统写法是 if-else 或虚函数,运行时选择有开销且不灵活。

实现结构

graph TD
    CON["<b>Context</b><br/>+operate() void"]
    FAS["<b>FastStrategy</b><br/>+algorithm() void"]
    ACC["<b>AccurateStrategy</b><br/>+algorithm() void"]
    LOG["<b>LoggingStrategy</b><br/>+algorithm() void"]
    CON --> FAS|"模板参数"|
    FAS --> CON|"模板参数"|
    CON --> ACC|"编译期绑定"|
    ACC --> CON|"编译期绑定"|
    CON --> LOG|"零开销"|
    LOG --> CON|"零开销"|

核心代码

// strategy.hpp
#pragma once
#include <type_traits>

namespace design_patterns::strategy {

// 策略 1:默认排序
struct StdSort {
    template <typename T>
    static void sort(std::vector<T>& v) {
        std::sort(v.begin(), v.end());
    }
};

// 策略 2:稳定排序
struct StableSort {
    template <typename T>
    static void sort(std::vector<T>& v) {
        std::stable_sort(v.begin(), v.end());
    }
};

// 策略 3:并行排序 (C++17)
struct ParallelSort {
    template <typename T>
    static void sort(std::vector<T>& v) {
        std::sort(std::execution::par, v.begin(), v.end());
    }
};

// Context:编译期绑定策略
template <typename SortPolicy = StdSort>
class Sorter {
public:
    template <typename T>
    void sort(std::vector<T>& data) {
        SortPolicy::sort(data);
    }
};

// 运行时策略选择(类型擦除版本,有额外开销,但灵活)
class RuntimeSorter {
public:
    using SortFunc = std::function<void(std::vector<int>&)>;

    explicit RuntimeSorter(SortFunc f) : sort_(std::move(f)) {}

    void sort(std::vector<int>& v) { sort_(v); }

private:
    SortFunc sort_;
};

} // namespace design_patterns::strategy

即插即用

#include "strategy.hpp"
using namespace design_patterns::strategy;

// 编译期选择 — 零开销,但编译后不可换
Sorter<StableSort> sorter;
sorter.sort(myData);

// 运行时选择 — 有 function 开销,但灵活
RuntimeSorter sorter([](auto& v) { std::ranges::sort(v); });
sorter.sort(myData);

使用示例

// 写一个日志系统,策略控制输出目标
struct FileLogger {
    static void log(std::string_view msg) { /* 写文件 */ }
};
struct ConsoleLogger {
    static void log(std::string_view msg) { std::cout << msg; }
};

template <typename LoggerPolicy = ConsoleLogger>
class App {
public:
    void run() {
        LoggerPolicy::log("App started\n");
        // ...
    }
};

// 开发时用控制台,发布时用文件 —— 改一行模板参数即可
App<FileLogger> prod;

常见陷阱

陷阱 解决
策略对象有状态 用实例而非静态方法;模板参数存策略对象而非类型
策略数量爆炸 组合优于排列:每个维度一个策略模板参数
编译期策略无法运行时切换 提供类型擦除版作为补充

3. Template Method 组件 — CRTP 静态多态

意图与痛点

你想定义一个算法骨架,让子类填充细节步骤。经典面向对象的 virtual + 继承方案有虚函数调用开销,且子类可能忘记调用基类的钩子。

实现结构

graph TD
    ALG["<b>AlgorithmBase</b><br/>+run() void<br/>#step1() void<br/>#step2() void<br/>#step3() void<br/>-hook() void"]
    CON["<b>ConcreteAlgo</b><br/>+step1() void<br/>+step3() void"]
    ALG --> CON|CRTP|
    CON --> ALG|CRTP|

核心代码

// template_method.hpp
#pragma once

namespace design_patterns::template_method {

// CRTP 基类:定义骨架,子类通过静态多态填充
template <typename Derived>
class DataProcessor {
public:
    // 骨架方法 — 算法流程固定
    void process() {
        static_cast<Derived*>(this)->validate();
        static_cast<Derived*>(this)->transform();
        static_cast<Derived*>(this)->save();
    }

    // 默认钩子:子类可覆盖
    void validate() {
        // 默认不做额外验证
    }

protected:
    // 编译期保证子类必须实现这些
    ~DataProcessor() = default;
};

// 具体实现
class CsvProcessor : public DataProcessor<CsvProcessor> {
public:
    void validate() {
        // CSV 特有验证
    }
    void transform() {
        // 解析 CSV
    }
    void save() {
        // 写到数据库
    }
};

class JsonProcessor : public DataProcessor<JsonProcessor> {
public:
    void transform() {
        // 解析 JSON
    }
    void save() {
        // 写回文件
    }
};

// 骨架工厂函数
template <typename Processor>
void runPipeline(Processor& p) {
    p.process();
}

} // namespace design_patterns::template_method

即插即用

#include "template_method.hpp"
using namespace design_patterns::template_method;

CsvProcessor csv;
csv.process();     // 编译期分发,零虚函数开销

JsonProcessor json;
runPipeline(json); // 泛型算法骨架

进阶:编译期钩子检查

// 用 concept (C++20) 检查子类是否实现了必要方法
template <typename T>
concept Processor = requires(T t) {
    { t.transform() } -> std::same_as<void>;
    { t.save() } -> std::same_as<void>;
};

template <Processor Derived>
class CheckedProcessor {
    // ...
};

常见陷阱

陷阱 解决
CRTP 不能统一存到容器 加一个类型擦除的包装层
子类忘记实现某步骤 C++20 concept 做编译期检查;或基类默认抛异常
骨架流程变复杂 拆成多个小骨架,组合而非继承

4. State 组件 — std::variant 状态机

意图与痛点

对象的行为随内部状态改变。传统方案是 enum + switch-case,状态一多就变成意大利面条,添加新状态要改所有 switch。

实现结构

stateDiagram-v2
    [*] --> Idle
    Idle --> Loading : start()
    Loading --> Running : data_ready()
    Loading --> Error : timeout()
    Running --> Paused : pause()
    Paused --> Running : resume()
    Running --> Idle : stop()
    Error --> Idle : reset()

核心代码

// state.hpp
#pragma once
#include <variant>
#include <string>

namespace design_patterns::state {

// 定义状态类型
struct Idle {};
struct Loading { int progress = 0; };
struct Running { std::string data; };
struct Paused { std::string snapshot; };
struct Error { std::string message; };

// 状态机:std::variant 为核心
class TaskStateMachine {
public:
    using State = std::variant<Idle, Loading, Running, Paused, Error>;

    // 状态转移 — 访问者模式自动分发
    void start() {
        std::visit([this](auto& s) { handleStart(s); }, state_);
    }

    void dataReady(std::string data) {
        std::visit([this, &data](auto& s) { handleDataReady(s, data); }, state_);
    }

    void pause() {
        std::visit([this](auto& s) { handlePause(s); }, state_);
    }

    void resume() {
        std::visit([this](auto& s) { handleResume(s); }, state_);
    }

    void stop() {
        std::visit([this](auto& s) { handleStop(s); }, state_);
    }

    void timeout() {
        std::visit([this](auto& s) { handleTimeout(s); }, state_);
    }

    void reset() {
        std::visit([this](auto& s) { handleReset(s); }, state_);
    }

    const State& state() const { return state_; }

private:
    State state_ = Idle{};

    // 每个状态-事件组合的处理
    void handleStart(Idle&)  { state_ = Loading{0}; }
    void handleStart(auto&)  { /* 忽略:不在 Idle 状态无法启动 */ }

    void handleDataReady(Loading& l, std::string& d) {
        state_ = Running{std::move(d)};
    }
    void handleDataReady(auto&, std::string&) {}

    void handleTimeout(Loading&) { state_ = Error{"加载超时"}; }
    void handleTimeout(auto&) {}

    void handlePause(Running& r) { state_ = Paused{r.data}; }
    void handlePause(auto&) {}

    void handleResume(Paused& p) { state_ = Running{p.snapshot}; }
    void handleResume(auto&) {}

    void handleStop(Running&)   { state_ = Idle{}; }
    void handleStop(Paused&)    { state_ = Idle{}; }
    void handleStop(auto&) {}

    void handleReset(Error&) { state_ = Idle{}; }
    void handleReset(auto&) {}
};

} // namespace design_patterns::state

即插即用

#include "state.hpp"
using namespace design_patterns::state;

TaskStateMachine fsm;
fsm.start();                    // Idle → Loading
fsm.dataReady("数据来了");      // Loading → Running
fsm.pause();                    // Running → Paused
fsm.resume();                   // Paused → Running
fsm.stop();                     // Running → Idle

进阶:状态转移表

// 编译期状态转移矩阵
template <typename From, typename Event>
struct Transition { using To = void; }; // 默认:非法转移

template <> struct Transition<Idle, struct StartEvent> { using To = Loading; };
template <> struct Transition<Loading, struct DataEvent> { using To = Running; };
// ... 转移表集中管理,新状态只需加特化

常见陷阱

陷阱 解决
variant 状态太多编译慢 超过 10 个状态考虑分层状态机
状态转移遗漏导致数据丢失 std::visitauto& 默认分支记日志
状态带数据导致 variant 体积大 大数据成员用 shared_ptr 包装

5. Chain of Responsibility 组件 — 拦截器管道

意图与痛点

一条请求需要经过多层处理:HTTP 中间件(认证→日志→限流→实际处理)、事件处理链(校验→转换→存储→通知)。传统写法是层层嵌套调用,添加新处理步骤需要修改调用链代码。

实现结构

graph TD
    HAN["<b>Handler</b><br/>+handle(req) optional~Response~<br/>+setNext(h) void"]
    AUT["<b>AuthHandler</b><br/>+handle(req) optional~Response~"]
    LOG["<b>LogHandler</b><br/>+handle(req) optional~Response~"]
    RAT["<b>RateLimitHandler</b><br/>+handle(req) optional~Response~"]
    COR["<b>CoreHandler</b><br/>+handle(req) optional~Response~"]
    HAN -.-> AUT|-.-|
    AUT -.-> HAN|-.-|
    HAN -.-> LOG|-.-|
    LOG -.-> HAN|-.-|
    HAN -.-> RAT|-.-|
    RAT -.-> HAN|-.-|
    HAN -.-> COR|-.-|
    COR -.-> HAN|-.-|
    HAN --> AUT|next|
    AUT --> HAN|next|
    LOG --> HAN|next|
    HAN --> LOG|next|
    RAT --> HAN|next|
    HAN --> RAT|next|
    COR --> HAN|next|

核心代码

// chain_of_responsibility.hpp
#pragma once
#include <memory>
#include <optional>

namespace design_patterns::chain {

template <typename Request, typename Response = void>
class Handler {
public:
    virtual ~Handler() = default;

    void setNext(std::unique_ptr<Handler> next) {
        next_ = std::move(next);
    }

    std::optional<Response> handle(const Request& req) {
        // 先自己处理
        auto result = process(req);
        if (result.has_value()) {
            return result;        // 自己处理完了就返回
        }
        // 自己不处理,交给下一个
        if (next_) {
            return next_->handle(req);
        }
        return std::nullopt;      // 链尾,无人处理
    }

protected:
    virtual std::optional<Response> process(const Request& req) = 0;

private:
    std::unique_ptr<Handler> next_;
};

// --- 管道式变体:每个处理器都执行(不中断) ---
template <typename Context>
class Pipeline {
public:
    using Step = std::function<void(Context&)>;

    Pipeline& addStep(Step step) {
        steps_.push_back(std::move(step));
        return *this;
    }

    void execute(Context& ctx) {
        for (auto& step : steps_) step(ctx);
    }

private:
    std::vector<Step> steps_;
};

} // namespace design_patterns::chain

即插即用

#include "chain_of_responsibility.hpp"
using namespace design_patterns::chain;

// 示例:HTTP 请求处理链
struct HttpRequest { std::string token, path; };

class AuthChecker : public Handler<HttpRequest, std::string> {
protected:
    std::optional<std::string> process(const HttpRequest& req) override {
        if (req.token.empty()) return std::string("401 Unauthorized");
        return std::nullopt; // 通过
    }
};

// 管道构建
auto auth = std::make_unique<AuthChecker>();
auto log  = std::make_unique<LogHandler>();
auth->setNext(std::move(log));
auto resp = auth->handle(request);

使用示例

// 管道式:中间件全执行
Pipeline<HttpRequest> pipeline;
pipeline.addStep([](auto& req) { req.path = sanitize(req.path); })
       .addStep([](auto& req) { logRequest(req); })
       .addStep([](auto& req) { checkRate(req); });
pipeline.execute(req);

常见陷阱

陷阱 解决
链过长性能差 考虑使用数组 + 索引代替链表
某处理器忘了传下去 统一基类管理传递逻辑(如上例)
管道式需要中断 让 Step 返回 bool(继续/停止)

6. Mediator 组件 — 类型安全事件总线

意图与痛点

多个组件互相通信——UI 按钮点击→数据模块更新→日志模块记录→网络模块发送。直接耦合会让代码变成蜘蛛网。需要一个中介者统一管理消息。

实现结构

graph TD
    EVE["<b>EventBus</b><br/>-map~type_index, vector~handler~~ handlers<br/>+emit~Event~(e) void<br/>+on~Event~(handler) Connection"]
    CON["<b>Connection</b><br/>+disconnect() void"]
    BUT["<b>Button</b><br/>+click() void"]
    DAT["<b>DataModel</b><br/>+onClick(e) void"]
    LOG["<b>Logger</b><br/>+onClick(e) void"]
    EVE --> CON
    CON --> EVE
    EVE --> BUT|emit(ClickEvent)|
    BUT --> EVE|emit(ClickEvent)|
    EVE --> DAT|notify|
    DAT --> EVE|notify|
    EVE --> LOG|notify|
    LOG --> EVE|notify|

核心代码

// mediator.hpp
#pragma once
#include <functional>
#include <memory>
#include <typeindex>
#include <unordered_map>
#include <vector>

namespace design_patterns::mediator {

// RAII 连接句柄
class Connection {
public:
    using DisconnectFn = std::function<void()>;

    explicit Connection(DisconnectFn fn) : disconnect_(std::move(fn)) {}
    ~Connection() { disconnect_(); }

    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;
    Connection(Connection&&) = default;
    Connection& operator=(Connection&&) = default;

    void disconnect() { disconnect_(); }

private:
    DisconnectFn disconnect_;
};

// 类型安全事件总线
class EventBus {
public:
    template <typename Event>
    using Handler = std::function<void(const Event&)>;

    // 注册监听
    template <typename Event>
    Connection on(Handler<Event> handler) {
        auto& handlers = getHandlers<Event>();
        handlers.push_back(std::move(handler));
        auto idx = handlers.size() - 1;

        return Connection([this, idx]() {
            auto& h = getHandlers<Event>();
            if (idx < h.size()) h[idx] = nullptr; // 惰性删除
        });
    }

    // 发送事件
    template <typename Event>
    void emit(const Event& event) {
        for (auto& h : getHandlers<Event>()) {
            if (h) h(event);
        }
        // 清理已断开的 handler
        auto& handlers = getHandlers<Event>();
        handlers.erase(
            std::remove(handlers.begin(), handlers.end(), nullptr),
            handlers.end());
    }

private:
    using HandlerList = std::vector<std::function<void(const void*)>>;

    template <typename Event>
    std::vector<Handler<Event>>& getHandlers() {
        auto key = std::type_index(typeid(Event));
        if (!storage_.contains(key)) {
            storage_[key] = std::vector<Handler<Event>>{};
        }
        return *reinterpret_cast<std::vector<Handler<Event>>*>(&storage_[key]);
    }

    std::unordered_map<std::type_index, HandlerList> storage_;
};

} // namespace design_patterns::mediator

即插即用

#include "mediator.hpp"
using namespace design_patterns::mediator;

// 定义事件 — 可以是任意 struct
struct ButtonClick { int x, y; };
struct DataChanged { std::string field, value; };

EventBus bus;

// 模块 A:发送
bus.emit(ButtonClick{10, 20});

// 模块 B:接收
auto conn = bus.on<ButtonClick>([](const auto& e) {
    std::cout << "Clicked at " << e.x << ", " << e.y;
});

使用示例

// 模拟 UI + 数据 + 日志的解耦
EventBus bus;

// 日志模块
auto c1 = bus.on<ButtonClick>([](auto&) { log("button clicked"); });
auto c2 = bus.on<DataChanged>([](auto& e) { log("data: " + e.field); });

// 数据模块
auto c3 = bus.on<ButtonClick>([&](auto&) { model->save(); });

// UI 模块只管发
button.onClick = [&]() { bus.emit(ButtonClick{100, 50}); };

进阶

  • 优先级队列: 给 handler 加权,按优先级调用
  • 异步事件: emit 投递到线程池
  • 事件溯源: 记录所有事件,支持回放

常见陷阱

陷阱 解决
handler 内修改 handler 列表 用副本遍历;或标记删除
事件类型太多编译慢 用基类 Event + dynamic_cast(仅必要时)
循环事件(A 发 B → B 发 A) 加递归深度限制

7. Memento 组件 — 状态快照与回滚

意图与痛点

你需要保存一个对象的内部状态,以便以后恢复——文档的 Ctrl+Z、游戏的存档、配置的历史版本。如果直接暴露对象内部结构,封装就被破坏了。

实现结构

graph TD
    ORI["<b>Originator</b><br/>-state string<br/>+save() Memento<br/>+restore(m) void<br/>+setState(s) void<br/>+getState() string"]
    MEM["<b>Memento</b><br/>-state string<br/>-timestamp time_point<br/>+state() string<br/>+timestamp() time_point"]
    CAR["<b>Caretaker</b><br/>-history vector~Memento~<br/>+push(m) void<br/>+pop() Memento<br/>+undo() void<br/>+redo() void"]
    ORI --> MEM|creates|
    MEM --> ORI|creates|
    MEM --> CAR|stores|
    CAR --> MEM|stores|

核心代码

// memento.hpp
#pragma once
#include <chrono>
#include <string>
#include <vector>

namespace design_patterns::memento {

// 备忘录:不可变快照
class Memento {
public:
    using Timestamp = std::chrono::system_clock::time_point;

    Memento(std::string state, Timestamp ts = std::chrono::system_clock::now())
        : state_(std::move(state)), timestamp_(ts) {}

    const std::string& state() const { return state_; }
    Timestamp timestamp() const { return timestamp_; }

private:
    std::string state_;
    Timestamp timestamp_;
};

// 原始对象:自己负责保存和恢复
class Originator {
public:
    void setState(std::string s) { state_ = std::move(s); }
    const std::string& state() const { return state_; }

    Memento save() const { return Memento(state_); }
    void restore(const Memento& m) { state_ = m.state(); }

private:
    std::string state_;
};

// 管理者:持有历史记录
class Caretaker {
public:
    void push(Memento m) {
        history_.push_back(std::move(m));
        // 新快照到来,清空 redo 记录
        redoIndex_ = history_.size();
    }

    bool canUndo() const { return history_.size() > 1 && redoIndex_ > 0; }
    bool canRedo() const { return redoIndex_ < history_.size(); }

    std::optional<Memento> undo() {
        if (!canUndo()) return std::nullopt;
        redoIndex_--;
        return history_[redoIndex_];
    }

    std::optional<Memento> redo() {
        if (!canRedo()) return std::nullopt;
        return history_[redoIndex_++];
    }

    size_t size() const { return history_.size(); }

private:
    std::vector<Memento> history_;
    size_t redoIndex_ = 0;
};

} // namespace design_patterns::memento

即插即用

#include "memento.hpp"
using namespace design_patterns::memento;

Originator doc;
Caretaker history;

doc.setState("初稿");
history.push(doc.save());

doc.setState("第二版");
history.push(doc.save());

doc.setState("第三版");

// 回退
if (auto m = history.undo()) { doc.restore(*m); }  // 回到第二版
if (auto m = history.undo()) { doc.restore(*m); }  // 回到初稿
if (auto m = history.redo()) { doc.restore(*m); }  // 回到第二版

进阶:泛型快照

// 任意可序列化类型的快照管理器
template <typename T>
    requires std::copyable<T>
class SnapshotManager {
public:
    void checkpoint(const T& obj) {
        snapshots_.push_back(obj);
        index_ = snapshots_.size();
    }

    std::optional<T> undo() { /* ... */ }
    std::optional<T> redo() { /* ... */ }

private:
    std::vector<T> snapshots_;
    size_t index_ = 0;
};

常见陷阱

陷阱 解决
快照太大(完整拷贝) 增量快照:只存变化部分
快照持有裸指针 序列化为值类型或 ID 引用
快照频率过高 合并策略:N 秒内多次修改只存一个

8. Iterator 组件 — 泛型迭代器适配

意图与痛点

你要遍历一棵树、一个自定义容器、或者一个流式数据源。每次写 for 循环都要记住内部结构,换一种遍历方式要写完全不同的代码。STL 的迭代器协议提供了统一的访问接口。

实现结构

graph TD
    RAN["<b>RangeAdaptor</b><br/>+begin() Iterator<br/>+end() Iterator<br/>+filter(pred) FilterView<br/>+transform(fn) TransformView"]
    ITE["<b>Iterator</b><br/>+operator++() Iterator&<br/>+operator*() T&<br/>+operator!=() bool"]
    TRE["<b>TreeNode</b><br/>+value T<br/>+children vector~Node~"]
    TRE["<b>TreeIterator</b><br/>-stack~Node*~ nodes<br/>+operator++()<br/>+operator*()"]
    RAN --> ITE
    ITE --> RAN
    ITE --> TRE|traverses|
    TRE --> ITE|traverses|

核心代码

// iterator.hpp
#pragma once
#include <iterator>
#include <stack>
#include <vector>

namespace design_patterns::iterator {

// 树节点
template <typename T>
struct TreeNode {
    T value;
    std::vector<TreeNode<T>> children;

    explicit TreeNode(T v) : value(std::move(v)) {}
};

// 深度优先迭代器
template <typename T>
class TreeIterator {
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = T*;
    using reference = T&;

    explicit TreeIterator(TreeNode<T>* root = nullptr) {
        if (root) stack_.push(root);
    }

    reference operator*() const { return stack_.top()->value; }
    pointer operator->() const { return &stack_.top()->value; }

    TreeIterator& operator++() {
        auto* node = stack_.top();
        stack_.pop();
        // 反向入栈,保持从左到右
        for (auto it = node->children.rbegin(); it != node->children.rend(); ++it) {
            stack_.push(&(*it));
        }
        return *this;
    }

    bool operator==(const TreeIterator& other) const {
        return stack_.empty() == other.stack_.empty();
    }
    bool operator!=(const TreeIterator& other) const { return !(*this == other); }

private:
    std::stack<TreeNode<T>*> stack_;
};

// 树的可迭代包装
template <typename T>
class IterableTree {
public:
    explicit IterableTree(TreeNode<T>& root) : root_(&root) {}

    TreeIterator<T> begin() { return TreeIterator<T>(root_); }
    TreeIterator<T> end()   { return TreeIterator<T>(); }

private:
    TreeNode<T>* root_;
};

} // namespace design_patterns::iterator

即插即用

#include "iterator.hpp"
using namespace design_patterns::iterator;

auto root = TreeNode<std::string>("root");
root.children.push_back(TreeNode<std::string>("A"));
root.children.push_back(TreeNode<std::string>("B"));
root.children[0].children.push_back(TreeNode<std::string>("A1"));

// 现在树支持 range-for!
for (auto& value : IterableTree(root)) {
    std::cout << value << "\n";  // root → A → A1 → B
}

进阶:视图适配器

template <typename Iter>
class FilterView {
    Iter begin_, end_;
    std::function<bool(const typename Iter::value_type&)> pred_;
public:
    // 用 STL filter_iterator 模式实现
    // C++20 直接用 std::views::filter
};

常见陷阱

陷阱 解决
迭代器失效(修改容器) 文档标注;或用 COW
树很深时栈溢出 改用堆上的显式栈(非递归)
自定义迭代器与 STL 不兼容 确保定义全部 5 个 typedef

9. Visitor 组件 — std::variant + std::visit

意图与痛点

你需要对一个类层次结构中的每个具体类型执行不同操作——比如 AST 遍历、序列化、语义分析。经典的虚函数 Visitor 模式需要给每个类加 accept(),侵入性强。

C++17 引入的 std::variant + std::visit 提供了更轻量的替代方案。

实现结构

graph TD
    AST["<b>ASTNode</b><br/>+Number<br/>+Plus<br/>+Multiply"]
    EVA["<b>Evaluator</b><br/>+operator()(Number) double<br/>+operator()(Plus) double<br/>+operator()(Multiply) double"]
    PRI["<b>Printer</b><br/>+operator()(Number) string<br/>+operator()(Plus) string<br/>+operator()(Multiply) string"]
    AST --> EVA|std::visit|
    EVA --> AST|std::visit|
    AST --> PRI|std::visit|
    PRI --> AST|std::visit|

核心代码

// visitor.hpp
#pragma once
#include <variant>
#include <string>
#include <memory>

namespace design_patterns::visitor {

// 定义 AST 节点类型(替代继承体系)
struct Number  { double value; };
struct Plus    { std::unique_ptr<Number> left, right; };
struct Multiply { std::unique_ptr<Number> left, right; };

using ASTNode = std::variant<Number, Plus, Multiply>;

// Visitor 1:求值
struct Evaluator {
    double operator()(const Number& n) const {
        return n.value;
    }
    double operator()(const Plus& p) const {
        return std::visit(Evaluator{}, ASTNode{*p.left})
             + std::visit(Evaluator{}, ASTNode{*p.right});
    }
    double operator()(const Multiply& m) const {
        return std::visit(Evaluator{}, ASTNode{*m.left})
             * std::visit(Evaluator{}, ASTNode{*m.right});
    }
};

// Visitor 2:打印
struct Printer {
    std::string operator()(const Number& n) const {
        return std::to_string(n.value);
    }
    std::string operator()(const Plus& p) const {
        return "(" + std::visit(Printer{}, ASTNode{*p.left})
             + " + "
             + std::visit(Printer{}, ASTNode{*p.right}) + ")";
    }
    std::string operator()(const Multiply& m) const {
        return "(" + std::visit(Printer{}, ASTNode{*m.left})
             + " * "
             + std::visit(Printer{}, ASTNode{*m.right}) + ")";
    }
};

// 工具函数:一行调用
template <typename Visitor>
auto visit(const ASTNode& node, Visitor&& v) {
    return std::visit(std::forward<Visitor>(v), node);
}

} // namespace design_patterns::visitor

即插即用

#include "visitor.hpp"
using namespace design_patterns::visitor;

// 构建 AST: (3 + 5) * 2
auto left = std::make_unique<Number>(3.0);
auto right = std::make_unique<Number>(5.0);
auto plus = Plus{std::move(left), std::move(right)};
auto ast = ASTNode{std::move(plus)};

// 求值
double result = visit(ast, Evaluator{});     // 8.0

// 打印
std::string expr = visit(ast, Printer{});    // "(3.0 + 5.0)"

进阶:泛型 Visitor 工具

// 过载模式 — 用 lambda 组合 visitor
template <typename... Ts>
struct overload : Ts... { using Ts::operator()...; };

// 无需定义结构体,直接 lambda
auto visitor = overload{
    [](const Number& n)  { return n.value; },
    [](const Plus& p)    { return visit(ASTNode{*p.left}, visitor)
                                 + visit(ASTNode{*p.right}, visitor); },
    [](const Multiply& m){ return visit(ASTNode{*m.left}, visitor)
                                 * visit(ASTNode{*m.right}, visitor); },
};
double result = visit(ast, visitor);

常见陷阱

陷阱 解决
variant 递归定义需要指针 unique_ptrshared_ptr 包装子节点
std::visit 必须穷举所有类型 overload + auto 默认分支
新增类型需要改所有 visitor variant 加新类型时编译报错,强制更新

组件总览

graph TB
    subgraph "行为型组件工具箱"
        CMD[Command<br/>可撤销命令]
        STG[Strategy<br/>策略替换]
        TMP[Template Method<br/>骨架复用]
        STA[State<br/>状态机]
        CHN[Chain<br/>拦截器管道]
        MED[Mediator<br/>事件总线]
        MEM[Memento<br/>快照回滚]
        ITR[Iterator<br/>泛型遍历]
        VIS[Visitor<br/>variant 分发]
    end

    CMD --> |配合| MEM
    MED --> |配合| CMD
    CHN --> |配合| STG
    STA --> |配合| VIS

总结

9 个行为型组件,核心工程的共同点:

  1. 零侵入: 每个都是单一头文件,#include 即用
  2. 类型安全: 模板 + concept + variant 在编译期拦住误用
  3. 现代 C++: 不写 new/delete,RAII 管理所有资源(Connection 句柄、命令栈、快照历史)
  4. 可组合: Command + Memento 实现编辑器 undo,Mediator + Chain 实现中间件系统

下一篇预告: 并发组件扩展 —— Active Object、Monitor Object、Thread Pool 的 C++ 封装。


本文属于「C++ 组件架构实战」课程系列,所有代码 MIT 协议开源。