首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >前端开发者快速掌握 C++:设计模式实战(行为型)

前端开发者快速掌握 C++:设计模式实战(行为型)

原创
作者头像
骑猪耍太极
发布2026-05-19 17:09:04
发布2026-05-19 17:09:04
930
举报
文章被收录于专栏:AI编程之旅AI编程之旅

上一篇讲了"怎么创建和组织对象",这一篇讲"对象之间怎么协作"。行为型模式决定了流程怎么编排、事件怎么分发、操作怎么记录。


一、模板方法模式 — 固定流程,可变细节

基类定死流程步骤,子类只填充其中的可变环节。

C++ 实现 — 以数据导出为例

代码语言:cpp
复制
class DataExporter {
public:
    // 固定流程(不可 override)
    void Export(const std::string& path) {
        auto data = FetchData();        // 步骤 1:获取数据(固定)
        auto formatted = Format(data);   // 步骤 2:格式化(子类决定)
        WriteFile(path, formatted);      // 步骤 3:写文件(固定)
        OnExportDone(path);              // 步骤 4:完成通知(子类可选)
    }
    virtual ~DataExporter() = default;

protected:
    virtual std::string Format(const std::vector<Record>& data) = 0;  // 必须实现
    virtual void OnExportDone(const std::string& path) {}              // 可选钩子

private:
    std::vector<Record> FetchData() { /* 从数据库读 */ }
    void WriteFile(const std::string& path, const std::string& content) { /* 写磁盘 */ }
};

class CsvExporter : public DataExporter {
protected:
    std::string Format(const std::vector<Record>& data) override {
        // 格式化为 CSV
    }
};

class JsonExporter : public DataExporter {
protected:
    std::string Format(const std::vector<Record>& data) override {
        // 格式化为 JSON
    }
    void OnExportDone(const std::string& path) override {
        NotifyUser("导出完成: " + path);
    }
};

前端类比

代码语言:typescript
复制
// React 类组件 — 框架定义了 mount → render → didMount 的流程
// 你只填 render() 和 componentDidMount()

// Express 的 res.send() 内部也是模板方法:
// 设置 Content-Type → 序列化 body → 发送 → 触发 finish 事件

关键点

  • 基类流程方法不加 virtual(不希望子类改流程)
  • 钩子方法加 virtual(允许子类自定义)
  • 有默认实现的钩子 = 可选覆盖;= 0 纯虚 = 必须覆盖

二、观察者模式 — 发布/订阅

发布者不知道谁在监听,订阅者不知道事件从哪来。

C++ 实现 — 以消息总线为例

代码语言:cpp
复制
class MessageBus {
public:
    using Handler = std::function<void(const std::string& payload)>;

    int Subscribe(const std::string& topic, Handler handler) {
        int id = next_id_++;
        topics_[topic].push_back({id, std::move(handler)});
        return id;  // 返回订阅 ID,用于取消
    }

    void Unsubscribe(int subscription_id) {
        for (auto& [topic, subs] : topics_) {
            subs.erase(std::remove_if(subs.begin(), subs.end(),
                [subscription_id](auto& s) { return s.id == subscription_id; }),
                subs.end());
        }
    }

    void Publish(const std::string& topic, const std::string& payload) {
        auto it = topics_.find(topic);
        if (it == topics_.end()) return;
        for (auto& sub : it->second) {
            sub.handler(payload);
        }
    }

private:
    struct Subscription { int id; Handler handler; };
    std::unordered_map<std::string, std::vector<Subscription>> topics_;
    int next_id_ = 0;
};

// 使用
MessageBus bus;
int id = bus.Subscribe("order.paid", [](const std::string& data) {
    SendNotification(data);
});
bus.Publish("order.paid", R"({"orderId": "123"})");
bus.Unsubscribe(id);  // 必须手动取消!

前端类比

代码语言:typescript
复制
// Node.js EventEmitter
emitter.on("order.paid", handler);
emitter.emit("order.paid", data);
emitter.off("order.paid", handler);

C++ 特有要点

  • 必须手动 Unsubscribe — JS 中 GC 回收对象时关联的闭包也会被回收,C++ 不会。忘了取消 = 访问已销毁对象 = 崩溃
  • 返回订阅 ID — 比存 handler 指针更可靠(std::function 不支持 == 比较)

三、策略模式 — 运行时切换算法

同一个操作有多种实现方式,运行时选一种。

C++ 实现 — 以压缩策略为例

代码语言:cpp
复制
class ICompressor {
public:
    virtual std::vector<uint8_t> Compress(const std::vector<uint8_t>& data) = 0;
    virtual std::vector<uint8_t> Decompress(const std::vector<uint8_t>& data) = 0;
    virtual ~ICompressor() = default;
};

class GzipCompressor : public ICompressor {
public:
    std::vector<uint8_t> Compress(const std::vector<uint8_t>& data) override { /* gzip */ }
    std::vector<uint8_t> Decompress(const std::vector<uint8_t>& data) override { /* gunzip */ }
};

class LZ4Compressor : public ICompressor {
public:
    std::vector<uint8_t> Compress(const std::vector<uint8_t>& data) override { /* lz4 */ }
    std::vector<uint8_t> Decompress(const std::vector<uint8_t>& data) override { /* lz4 */ }
};

class FileStorage {
public:
    FileStorage(std::unique_ptr<ICompressor> compressor)
        : compressor_(std::move(compressor)) {}

    void Save(const std::string& path, const std::vector<uint8_t>& data) {
        auto compressed = compressor_->Compress(data);
        WriteToDisk(path, compressed);
    }

private:
    std::unique_ptr<ICompressor> compressor_;
};

// 使用:根据场景选择
auto storage = FileStorage(std::make_unique<LZ4Compressor>());  // 速度优先
auto storage2 = FileStorage(std::make_unique<GzipCompressor>()); // 体积优先

前端类比

代码语言:typescript
复制
// 根据环境选择不同的存储策略
const storage = isServer
    ? new FileSystemStorage()
    : new LocalStorageAdapter();
storage.save(key, data);

优势

  • 新增压缩算法(如 Zstd)只需新增一个类,不改已有代码
  • 策略可通过配置文件/环境变量决定,无需重新编译

四、责任链模式 — 逐层尝试处理

请求沿链传递,每个节点决定"我处理"或"交给下一个"。

C++ 实现 — 以日志级别过滤为例

代码语言:cpp
复制
class LogHandler {
public:
    enum Level { DEBUG, INFO, WARN, ERROR };

    LogHandler(Level level) : level_(level) {}
    void SetNext(std::shared_ptr<LogHandler> next) { next_ = next; }

    void Log(Level level, const std::string& msg) {
        if (level >= level_) {
            Write(level, msg);
        }
        if (next_) {
            next_->Log(level, msg);
        }
    }
    virtual ~LogHandler() = default;

protected:
    virtual void Write(Level level, const std::string& msg) = 0;

private:
    Level level_;
    std::shared_ptr<LogHandler> next_;
};

class ConsoleLogger : public LogHandler {
public:
    ConsoleLogger() : LogHandler(DEBUG) {}
protected:
    void Write(Level level, const std::string& msg) override {
        std::cout << "[" << level << "] " << msg << std::endl;
    }
};

class FileLogger : public LogHandler {
public:
    FileLogger() : LogHandler(WARN) {}  // 只记录 WARN 以上
protected:
    void Write(Level level, const std::string& msg) override {
        // 写入日志文件
    }
};

class AlertLogger : public LogHandler {
public:
    AlertLogger() : LogHandler(ERROR) {}  // 只报警 ERROR
protected:
    void Write(Level level, const std::string& msg) override {
        SendAlert(msg);
    }
};

// 组装链
auto console = std::make_shared<ConsoleLogger>();
auto file = std::make_shared<FileLogger>();
auto alert = std::make_shared<AlertLogger>();
console->SetNext(file);
file->SetNext(alert);

// 使用
console->Log(ERROR, "Database connection lost");
// → ConsoleLogger 输出 → FileLogger 记录 → AlertLogger 报警

前端类比

代码语言:typescript
复制
// Express 中间件 — 经典责任链
app.use(corsMiddleware);   // 处理跨域,然后 next()
app.use(authMiddleware);   // 处理鉴权,然后 next()
app.use(routerMiddleware); // 处理路由

和模板方法的区别

  • 模板方法:固定的步骤 1→2→3→4,每步做什么由子类决定
  • 责任链:不确定谁来处理,按顺序问过去,谁能处理谁处理

五、命令模式 — 操作的记录与回放

把"操作"封装为对象,可以存储、撤销、重做。

C++ 实现 — 以文本编辑器为例

代码语言:cpp
复制
class ICommand {
public:
    virtual void Execute() = 0;
    virtual void Undo() = 0;
    virtual ~ICommand() = default;
};

class InsertTextCommand : public ICommand {
public:
    InsertTextCommand(Document& doc, int pos, const std::string& text)
        : doc_(doc), pos_(pos), text_(text) {}

    void Execute() override { doc_.Insert(pos_, text_); }
    void Undo() override { doc_.Delete(pos_, text_.size()); }

private:
    Document& doc_;
    int pos_;
    std::string text_;
};

class DeleteTextCommand : public ICommand {
public:
    DeleteTextCommand(Document& doc, int pos, int length)
        : doc_(doc), pos_(pos), length_(length) {}

    void Execute() override {
        deleted_text_ = doc_.GetText(pos_, length_);  // 记住被删的内容
        doc_.Delete(pos_, length_);
    }
    void Undo() override { doc_.Insert(pos_, deleted_text_); }

private:
    Document& doc_;
    int pos_, length_;
    std::string deleted_text_;
};

class CommandHistory {
public:
    void Execute(std::unique_ptr<ICommand> cmd) {
        cmd->Execute();
        history_.push_back(std::move(cmd));
        redo_stack_.clear();
    }

    void Undo() {
        if (history_.empty()) return;
        auto cmd = std::move(history_.back());
        history_.pop_back();
        cmd->Undo();
        redo_stack_.push_back(std::move(cmd));
    }

    void Redo() {
        if (redo_stack_.empty()) return;
        auto cmd = std::move(redo_stack_.back());
        redo_stack_.pop_back();
        cmd->Execute();
        history_.push_back(std::move(cmd));
    }

private:
    std::vector<std::unique_ptr<ICommand>> history_;
    std::vector<std::unique_ptr<ICommand>> redo_stack_;
};

前端类比

代码语言:typescript
复制
// Redux 本质就是命令模式
dispatch({ type: "INSERT_TEXT", pos: 5, text: "hello" });  // 命令对象
// Redux DevTools "Time Travel" = 命令回放/撤销

适用场景

场景

怎么用

编辑器撤销/重做

每个操作是一个 Command,Undo 执行反操作

宏录制

记录一系列命令,一键回放

事务

多个命令组成一组,失败时全部 Undo

操作日志

序列化命令到磁盘,用于审计或 Bug 复现


总结

模式

核心问题

C++ 关键实现

前端等价物

模板方法

统一流程,可变细节

虚函数钩子 + 非虚流程方法

React 生命周期

观察者

事件解耦

订阅 ID + 手动取消

EventEmitter

策略

算法可替换

接口 + unique_ptr

条件选择不同实现

责任链

逐层尝试

链表传递

Express 中间件

命令

操作可撤销/回放

Execute + Undo 封装

Redux Action


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、模板方法模式 — 固定流程,可变细节
    • C++ 实现 — 以数据导出为例
    • 前端类比
    • 关键点
  • 二、观察者模式 — 发布/订阅
    • C++ 实现 — 以消息总线为例
    • 前端类比
    • C++ 特有要点
  • 三、策略模式 — 运行时切换算法
    • C++ 实现 — 以压缩策略为例
    • 前端类比
    • 优势
  • 四、责任链模式 — 逐层尝试处理
    • C++ 实现 — 以日志级别过滤为例
    • 前端类比
    • 和模板方法的区别
  • 五、命令模式 — 操作的记录与回放
    • C++ 实现 — 以文本编辑器为例
    • 前端类比
    • 适用场景
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档