观察者模式是《Head First设计模式》中最经典的模式之一,也是我在实际C++项目中应用最频繁的设计模式。它定义了对象之间的一对多依赖关系,当一个对象(被观察者)状态发生改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。
我在一个实时数据监控系统中首次应用这个模式时,发现它完美解决了数据源与多个显示模块之间的动态耦合问题。比如当温度传感器数据变化时,需要同时更新曲线图、数字面板和报警器三个UI组件,而观察者模式让这种需求变得异常简单。
观察者模式包含两个核心角色:
在C++中,我通常这样实现基础框架:
cpp复制// 观察者基类
class Observer {
public:
virtual ~Observer() = default;
virtual void update(float temp, float humidity) = 0;
};
// 被观察者基类
class Subject {
public:
virtual ~Subject() = default;
virtual void registerObserver(Observer* o) = 0;
virtual void removeObserver(Observer* o) = 0;
virtual void notifyObservers() = 0;
};
以气象站为例,这是我在项目中实际使用的WeatherData实现:
cpp复制class WeatherData : public Subject {
private:
std::vector<Observer*> observers;
float temperature;
float humidity;
public:
void registerObserver(Observer* o) override {
observers.push_back(o);
}
void removeObserver(Observer* o) override {
observers.erase(std::remove(observers.begin(),
observers.end(), o),
observers.end());
}
void notifyObservers() override {
for (auto* o : observers) {
o->update(temperature, humidity);
}
}
void measurementsChanged() {
notifyObservers();
}
void setMeasurements(float temp, float humidity) {
this->temperature = temp;
this->humidity = humidity;
measurementsChanged();
}
};
原始实现中观察者的生命周期管理是个隐患。在我的实践中,改用shared_ptr/weak_ptr组合:
cpp复制class ModernSubject {
private:
std::vector<std::weak_ptr<Observer>> observers;
public:
void registerObserver(std::shared_ptr<Observer> o) {
observers.emplace_back(o);
}
void notifyObservers() {
auto it = observers.begin();
while (it != observers.end()) {
if (auto o = it->lock()) {
o->update(data);
++it;
} else {
it = observers.erase(it);
}
}
}
};
C++11之后,我更喜欢用std::function替代接口继承:
cpp复制class EventSubject {
using Callback = std::function<void(float, float)>;
std::vector<Callback> callbacks;
public:
void registerObserver(Callback cb) {
callbacks.push_back(cb);
}
void notifyObservers(float temp, float humidity) {
for (auto& cb : callbacks) {
cb(temp, humidity);
}
}
};
在实时系统中,不加控制的观察者通知会导致性能问题。我的解决方案:
cpp复制void onSensorUpdate() {
dirty = true;
// 每100ms检查一次
if (!timerRunning) {
startTimer(100ms);
}
}
void onTimer() {
if (dirty) {
notifyObservers();
dirty = false;
}
}
cpp复制void setMeasurements(float temp, float humidity) {
if (abs(temp - temperature) > 0.5 ||
abs(humidity - this->humidity) > 1.0) {
this->temperature = temp;
this->humidity = humidity;
notifyObservers();
}
}
在多线程环境中,我通常采用以下模式:
cpp复制class ThreadSafeSubject {
std::vector<std::weak_ptr<Observer>> observers;
mutable std::mutex mtx;
public:
void registerObserver(std::shared_ptr<Observer> o) {
std::lock_guard<std::mutex> lock(mtx);
observers.emplace_back(o);
}
void notifyObservers() {
std::vector<std::shared_ptr<Observer>> validObservers;
{
std::lock_guard<std::mutex> lock(mtx);
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto o = it->lock()) {
validObservers.push_back(o);
++it;
} else {
it = observers.erase(it);
}
}
}
for (auto& o : validObservers) {
o->update(data);
}
}
};
当观察者同时持有Subject引用时,会导致内存泄漏。我的解决方法:
cpp复制// 错误示例:相互持有shared_ptr
class BadObserver : public Observer {
std::shared_ptr<Subject> subject; // 循环引用!
};
// 正确做法:使用weak_ptr打破循环
class SafeObserver : public Observer {
std::weak_ptr<Subject> subject;
};
某些观察者可能有执行顺序要求。我的排序方案:
cpp复制void WeatherData::registerObserver(Observer* o, int priority) {
observers.insert(std::upper_bound(
observers.begin(), observers.end(), priority,
[](int val, auto&& item) {
return val < item.priority;
}),
{o, priority}
);
}
在复杂回调场景中,我遵循以下原则:
经典实现是推模型(Subject主动推送数据),但有时拉模型更合适:
cpp复制class PullObserver {
public:
virtual void update(Subject* subject) = 0;
};
void WeatherData::notifyObservers() {
for (auto o : observers) {
o->update(this); // 观察者自己拉取需要的数据
}
}
基于观察者模式,我构建过简单的事件总线系统:
cpp复制class EventBus {
std::unordered_map<EventType,
std::vector<EventHandler>> handlers;
public:
void subscribe(EventType type, EventHandler handler) {
handlers[type].push_back(handler);
}
void publish(const Event& event) {
for (auto& handler : handlers[event.type]) {
handler(event.data);
}
}
};
现代C++中,观察者模式可以很自然地与RxCpp等库结合:
cpp复制auto subject = rxcpp::subjects::subject<float>();
auto observable = subject.get_observable();
// 订阅
auto subscription = observable.subscribe(
[](float temp) { /* 处理数据 */ },
[]() { /* 完成处理 */ }
);
// 发布
subject.get_subscriber().on_next(23.5f);
频繁的观察者注册/注销会导致内存分配。我的优化方案:
cpp复制class HighPerformanceSubject {
moodycamel::ConcurrentQueue<Observer*> observers;
public:
void notifyObservers() {
Observer* observer;
while (observers.try_dequeue(observer)) {
observer->update(data);
// 处理完后回收观察者
observerPool.release(observer);
}
}
};
对于大量观察者,我采用分批通知策略:
cpp复制void batchNotify() {
const size_t batchSize = 10;
auto it = observers.begin();
while (it != observers.end()) {
std::vector<Observer*> batch;
for (size_t i = 0; i < batchSize && it != observers.end(); ++i, ++it) {
if (auto o = it->lock()) {
batch.push_back(o.get());
}
}
// 并行处理批次
std::for_each(std::execution::par, batch.begin(), batch.end(),
[](auto o) { o->update(data); });
}
}
我为观察者模式设计了专门的测试夹具:
cpp复制TEST(ObserverPattern, NotificationTest) {
WeatherData weather;
MockObserver obs1, obs2;
weather.registerObserver(&obs1);
weather.registerObserver(&obs2);
EXPECT_CALL(obs1, update(23.0f, 65.0f)).Times(1);
EXPECT_CALL(obs2, update(23.0f, 65.0f)).Times(1);
weather.setMeasurements(23.0f, 65.0f);
}
使用自定义删除器追踪观察者生命周期:
cpp复制template<typename T>
struct DebugDeleter {
void operator()(T* p) {
std::cout << "Deleting observer @" << p << "\n";
delete p;
}
};
using DebugObserverPtr = std::unique_ptr<Observer, DebugDeleter<Observer>>;
关键指标检查清单: