观察者模式的核心思想是建立被观察者和观察者之间的订阅关系,当被观察者的状态发生改变时,主动通知所有已订阅的观察者,让观察者执行对应的处理逻辑。这种机制可以有效降低对象之间的耦合度,让事件的产生和处理逻辑分离。
观察者模式的核心角色
实现C++观察者模式需要定义两个核心角色,分别是被观察者(Subject)和观察者(Observer),部分场景下还会增加事件类来封装通知信息。
被观察者角色
被观察者需要维护一个观察者列表,提供添加观察者、移除观察者、通知所有观察者的方法,同时保存自身的状态信息。当状态发生变化时,调用通知方法触发所有观察者的更新逻辑。
观察者角色
观察者是一个抽象接口或者具体类,定义统一的更新方法,当收到被观察者的通知时,执行对应的事件处理逻辑。不同的观察者可以实现不同的更新逻辑,实现事件处理的差异化。
完整C++实现代码
下面是一个简单的天气站例子,天气站作为被观察者,当温度发生变化时,通知所有订阅的显示设备(观察者)更新温度信息。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
// 观察者抽象接口
class Observer {
public:
virtual ~Observer() = default;
// 更新方法,接收温度参数
virtual void update(float temperature) = 0;
};
// 被观察者抽象接口
class Subject {
public:
virtual ~Subject() = default;
// 添加观察者
virtual void attach(Observer* observer) = 0;
// 移除观察者
virtual void detach(Observer* observer) = 0;
// 通知所有观察者
virtual void notify() = 0;
};
// 具体被观察者:天气站
class WeatherStation : public Subject {
private:
std::vector<Observer*> observers;
float temperature;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
auto it = std::find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}
void notify() override {
for (Observer* observer : observers) {
observer->update(temperature);
}
}
// 设置温度,触发通知
void setTemperature(float temp) {
temperature = temp;
notify();
}
};
// 具体观察者:温度显示设备
class TemperatureDisplay : public Observer {
private:
std::string name;
public:
TemperatureDisplay(const std::string& displayName) : name(displayName) {}
void update(float temperature) override {
std::cout << name << " 收到温度更新: " << temperature << " 摄氏度" << std::endl;
}
};
int main() {
// 创建被观察者
WeatherStation station;
// 创建两个观察者
TemperatureDisplay display1("客厅显示屏");
TemperatureDisplay display2("卧室显示屏");
// 订阅观察者
station.attach(&display1);
station.attach(&display2);
// 修改温度,触发通知
station.setTemperature(25.5f);
station.setTemperature(28.0f);
// 移除一个观察者
station.detach(&display1);
station.setTemperature(26.0f);
return 0;
}
代码说明
上述代码中,Observer是观察者抽象接口,所有具体观察者都需要实现update方法。Subject是被观察者抽象接口,定义了观察者管理的基础方法。WeatherStation作为具体被观察者,维护了观察者列表和温度状态,当调用setTemperature修改温度时,会调用notify方法通知所有观察者。TemperatureDisplay是具体观察者,收到通知后打印温度信息。
实际应用场景
观察者模式的事件监听与通知机制在很多场景都有应用,比如GUI开发中的按钮点击事件监听,当按钮被点击时通知所有注册的监听者执行对应逻辑;再比如消息队列中的订阅发布机制,生产者发布消息后,所有订阅该主题的消费者都会收到消息。使用这种模式可以让代码的结构更清晰,各个模块的职责更明确,降低模块之间的耦合度。
注意事项
- 观察者列表中存储的是观察者指针,需要注意观察者的生命周期,避免被观察者通知已经销毁的观察者导致程序崩溃。
- 如果通知逻辑比较复杂,可以考虑将通知操作放到单独的线程中执行,避免阻塞被观察者的状态更新流程。
- 当观察者数量较多时,通知所有观察者可能会有性能开销,可以根据实际需求优化通知逻辑,比如只通知状态发生变化的观察者。