上一篇讲了"怎么创建和组织对象",这一篇讲"对象之间怎么协作"。行为型模式决定了流程怎么编排、事件怎么分发、操作怎么记录。
基类定死流程步骤,子类只填充其中的可变环节。
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);
}
};// React 类组件 — 框架定义了 mount → render → didMount 的流程
// 你只填 render() 和 componentDidMount()
// Express 的 res.send() 内部也是模板方法:
// 设置 Content-Type → 序列化 body → 发送 → 触发 finish 事件virtual(不希望子类改流程)virtual(允许子类自定义)= 0 纯虚 = 必须覆盖发布者不知道谁在监听,订阅者不知道事件从哪来。
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); // 必须手动取消!// Node.js EventEmitter
emitter.on("order.paid", handler);
emitter.emit("order.paid", data);
emitter.off("order.paid", handler);std::function 不支持 == 比较)同一个操作有多种实现方式,运行时选一种。
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>()); // 体积优先// 根据环境选择不同的存储策略
const storage = isServer
? new FileSystemStorage()
: new LocalStorageAdapter();
storage.save(key, data);请求沿链传递,每个节点决定"我处理"或"交给下一个"。
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 报警// Express 中间件 — 经典责任链
app.use(corsMiddleware); // 处理跨域,然后 next()
app.use(authMiddleware); // 处理鉴权,然后 next()
app.use(routerMiddleware); // 处理路由把"操作"封装为对象,可以存储、撤销、重做。
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_;
};// Redux 本质就是命令模式
dispatch({ type: "INSERT_TEXT", pos: 5, text: "hello" }); // 命令对象
// Redux DevTools "Time Travel" = 命令回放/撤销场景 | 怎么用 |
|---|---|
编辑器撤销/重做 | 每个操作是一个 Command,Undo 执行反操作 |
宏录制 | 记录一系列命令,一键回放 |
事务 | 多个命令组成一组,失败时全部 Undo |
操作日志 | 序列化命令到磁盘,用于审计或 Bug 复现 |
模式 | 核心问题 | C++ 关键实现 | 前端等价物 |
|---|---|---|---|
模板方法 | 统一流程,可变细节 | 虚函数钩子 + 非虚流程方法 | React 生命周期 |
观察者 | 事件解耦 | 订阅 ID + 手动取消 | EventEmitter |
策略 | 算法可替换 | 接口 + | 条件选择不同实现 |
责任链 | 逐层尝试 | 链表传递 | Express 中间件 |
命令 | 操作可撤销/回放 | Execute + Undo 封装 | Redux Action |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。