在C++项目中,模块之间往往需要低耦合的通信方式。观察者模式通过定义一对多的依赖,让多个对象监听同一个主题对象的状态变化。发布订阅机制进一步引入了事件通道,使发送方和接收方互不感知。下面通过一个具体实例说明如何设计。

核心设计思路
我们采用抽象观察者接口与具体主题类的方式。主题维护一个观察者列表,提供订阅、取消订阅和通知方法。为了支持不同事件,使用枚举区分消息类型。
基础接口定义
先定义观察者抽象基类和事件枚举:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <functional>
// 事件类型
enum class EventType {
TemperatureChanged,
PressureChanged
};
// 观察者抽象接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(EventType type, const std::string& msg) = 0;
};
主题与发布订阅实现
主题类管理观察者并使用智能指针防止资源问题,notify函数遍历列表进行通知:
class Subject {
public:
void subscribe(std::shared_ptr<Observer> obs) {
observers.push_back(obs);
}
void unsubscribe(std::shared_ptr<Observer> obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void notify(EventType type, const std::string& msg) {
for (auto& obs : observers) {
if (obs) {
obs->update(type, msg);
}
}
}
private:
std::vector<std::shared_ptr<Observer>> observers;
};
具体观察者示例
实现一个控制台打印观察者,根据事件类型输出不同信息:
class ConsoleObserver : public Observer {
public:
void update(EventType type, const std::string& msg) override {
if (type == EventType::TemperatureChanged) {
std::cout << "[温度] " << msg << std::endl;
} else {
std::cout << "[压力] " << msg << std::endl;
}
}
};
使用方式与测试
在main函数中创建主题和观察者并完成订阅与发布:
int main() {
Subject sensor;
auto console = std::make_shared<ConsoleObserver>();
sensor.subscribe(console);
sensor.notify(EventType::TemperatureChanged, "25摄氏度");
sensor.notify(EventType::PressureChanged, "101千帕");
sensor.unsubscribe(console);
sensor.notify(EventType::TemperatureChanged, "不会再收到");
return 0;
}
进阶:基于函数的轻量订阅
如果不想定义过多类,可以用std::function实现更灵活的发布订阅,主题直接保存回调:
class Publisher {
public:
using Callback = std::function<void(const std::string&)>;
void addListener(Callback cb) {
callbacks.push_back(cb);
}
void publish(const std::string& data) {
for (auto& cb : callbacks) {
cb(data);
}
}
private:
std::vector<Callback> callbacks;
};
这种方式适合事件简单、观察者逻辑短小的场景,能减少类层级。实际工程中可结合std::mutex保证多线程安全,并用weak_ptr避免循环引用。通过上述设计,C++观察者模式与发布订阅机制即可清晰落地。