下面从设计思想、优缺点、适用场景三个维度,对比 回调函数对象、观察者模式、信号槽、消息总线​ 四种常见回调机制,并给出 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 / 模块解耦​ 👉 信号槽

  • 系统级解耦 / 插件化​ 👉 消息总线

更多推荐