C++必备设计模式:Callback、Observer、Signal-Slot、MessageBus 横向评测
下面从设计思想、优缺点、适用场景三个维度,对比 回调函数对象、观察者模式、信号槽、消息总线 四种常见回调机制,并给出 C++ 示例代码。
1️⃣ 回调函数对象(Callback Object)
✅ 核心思想
将“可调用的对象”(函数指针、std::function、lambda)作为参数传入,被调用方在特定时机执行它。
✅ 示例
#include <iostream>
#include <functional>
class Worker {
public:
void doWork(std::function<void(int)> callback) {
int result = 42;
callback(result);
}
};
int main() {
Worker w;
w.doWork([](int v) {
std::cout << "Result: " << v << std::endl;
});
}
✅ 优点
-
简单直观,学习成本低
-
零额外抽象,性能最好
-
非常适合 一次性、局部回调
❌ 缺点
-
强耦合(调用方必须知道回调签名)
-
不支持一对多
-
生命周期管理困难(悬空回调)
📌 使用场景
-
异步任务完成通知
-
策略模式、算法回调
-
工具函数、库内部回调
2️⃣ 观察者模式(Observer Pattern)
✅ 核心思想
一对多关系:Subject 维护 Observer 列表,状态变化时通知所有观察者。
✅ 示例
#include <iostream>
#include <vector>
class Observer {
public:
virtual void onNotify(int value) = 0;
};
class Subject {
std::vector<Observer*> observers;
public:
void add(Observer* o) { observers.push_back(o); }
void notify(int v) {
for (auto o : observers) o->onNotify(v);
}
};
class Printer : public Observer {
public:
void onNotify(int v) override {
std::cout << "Printer: " << v << std::endl;
}
};
✅ 优点
-
解耦 Subject 与 Observer
-
天然支持广播
-
符合开闭原则
❌ 缺点
-
接口侵入性强(必须继承)
-
生命周期管理复杂
-
通知顺序不可控
📌 使用场景
-
GUI 事件监听
-
状态变化广播
-
游戏引擎(事件系统)
3️⃣ 信号槽(Signal–Slot)
✅ 核心思想
信号(Signal)与槽(Slot)解耦连接,信号触发时自动调用所有连接的槽。
这里用 C++ 模拟 Qt 风格(不使用 Qt)。
✅ 示例(简化版)
#include <iostream>
#include <vector>
#include <functional>
class Signal {
std::vector<std::function<void(int)>> slots;
public:
void connect(std::function<void(int)> f) {
slots.push_back(f);
}
void emit(int v) {
for (auto& f : slots) f(v);
}
};
class Receiver {
public:
void onValue(int v) {
std::cout << "Received: " << v << std::endl;
}
};
int main() {
Signal signal;
Receiver r;
signal.connect([&](int v) { r.onValue(v); });
signal.emit(10);
}
✅ 优点
-
极低耦合(无需继承)
-
支持任意可调用对象
-
适合 UI / 组件通信
❌ 缺点
-
调试困难(调用链隐式)
-
性能略低于直接调用
-
容易形成“信号满天飞”
📌 使用场景
-
Qt / GUI 开发
-
组件解耦通信
-
异步事件响应
4️⃣ 消息总线(Message Bus)
✅ 核心思想
通过 统一消息中心 发布/订阅消息,完全解耦发送者与接收者。
✅ 示例(简化版)
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
struct MessageBus {
using Handler = std::function<void(const std::string&)>;
std::unordered_map<std::string, std::vector<Handler>> topics;
void subscribe(const std::string& topic, Handler h) {
topics[topic].push_back(h);
}
void publish(const std::string& topic, const std::string& msg) {
for (auto& h : topics[topic]) h(msg);
}
};
int main() {
MessageBus bus;
bus.subscribe("log", [](const std::string& m) {
std::cout << "[LOG] " << m << std::endl;
});
bus.publish("log", "Hello MessageBus");
}
✅ 优点
-
最大解耦(跨模块 / 跨线程)
-
易于扩展、替换模块
-
支持复杂系统架构
❌ 缺点
-
系统复杂度高
-
消息泛滥难追踪
-
性能与调试成本上升
📌 使用场景
-
插件系统
-
微服务 / 分布式系统
-
大型工程、模块解耦
📊 对比总结表
|
方式 |
耦合度 |
一对多 |
复杂度 |
典型场景 |
|---|---|---|---|---|
|
回调函数对象 |
高 |
❌ |
⭐ |
简单异步、策略 |
|
观察者模式 |
中 |
✅ |
⭐⭐ |
状态广播 |
|
信号槽 |
低 |
✅ |
⭐⭐ |
UI / 组件通信 |
|
消息总线 |
最低 |
✅ |
⭐⭐⭐⭐ |
大型系统 |
✅ 选型建议(经验法则)
-
一个函数 → 一个结果 👉 回调函数对象
-
一个对象 → 多个监听者 👉 观察者模式
-
UI / 模块解耦 👉 信号槽
-
系统级解耦 / 插件化 👉 消息总线
更多推荐
所有评论(0)