观察者模式——C++

发布时间:2026/9/5 3:51:38
观察者模式——C++ 观察者模式Observer Pattern观察者模式是一种行为型设计模式。它解决的问题很简单一个对象的状态发生变化时需要自动通知多个其他对象。例如Qt 的 signal / slot微信公众号发布文章 → 多个用户收到通知股票股票价格变化 → 多个界面自动刷新温度传感器温度变化 → 温度显示、报警器、记录器同时更新MVCModel 数据变化 → View 自动刷新一、先理解一个生活中的例子假设有一个气象站气象站 │ 温度发生变化 │ ┌─────┼─────┐ ↓ ↓ ↓ 温度显示 手机App 报警器气象站并不需要知道每个设备具体如何处理。它只需要通知所有观察者每个观察者自己决定收到通知后做什么。这就是观察者模式的核心思想。二、观察者模式的结构主要有两个角色1. Subject主题/被观察者负责保存观察者添加观察者删除观察者状态发生变化时通知观察者2. Observer观察者负责接收通知根据通知执行自己的操作结构Subject │ notify()│ ┌────────┼────────┐ ↓ ↓ ↓ ObserverA ObserverB ObserverC三、最简单的 C 实现我们模拟一个温度传感器。Observer.h#ifndef OBSERVER_H#define OBSERVER_Hclass Observer{public: virtual ~Observer(){}virtual void update(float temperature)0;};#endif这里定义观察者接口。所有观察者都必须实现update()Subject#ifndefSUBJECT_H#defineSUBJECT_H#includevector#includeobserver.hclassSubject{public:virtual~Subject(){}voidattach(Observer*observer){m_observers.push_back(observer);}voiddetach(Observer*observer){for(autoitm_observers.begin();it!m_observers.end();it){if(*itobserver){m_observers.erase(it);break;}}}protected:voidnotify(floattemperature){for(Observer*observer:m_observers){observer-update(temperature);}}private:std::vectorObserver*m_observers;};#endif四、实现温度传感器#ifndefTEMPERATURESENSOR_H#defineTEMPERATURESENSOR_H#includesubject.hclassTemperatureSensor:publicSubject{public:voidsetTemperature(floattemperature){m_temperaturetemperature;// 温度发生变化notify(m_temperature);}floattemperature()const{returnm_temperature;}private:floatm_temperature0;};#endif重点就是m_temperature temperature;notify(m_temperature);温度变化之后自动通知所有观察者。五、创建具体观察者温度显示器#includeiostream#includeobserver.hclassTemperatureDisplay:publicObserver{public:voidupdate(floattemperature)override{std::cout温度显示器当前温度 temperature ℃std::endl;}};报警器#includeiostream#includeobserver.hclassTemperatureAlarm:publicObserver{public:voidupdate(floattemperature)override{if(temperature30){std::cout报警器温度过高std::endl;}}};日志记录器#includeiostream#includeobserver.hclassTemperatureLogger:publicObserver{public:voidupdate(floattemperature)override{std::cout日志记录温度 temperature ℃std::endl;}};六、测试#includetemperaturesensor.hintmain(){TemperatureSensor sensor;TemperatureDisplay display;TemperatureAlarm alarm;TemperatureLogger logger;sensor.attach(display);sensor.attach(alarm);sensor.attach(logger);sensor.setTemperature(25);std::cout----------------std::endl;sensor.setTemperature(35);return0;}运行温度显示器当前温度 25 ℃日志记录温度 25 ℃温度显示器当前温度 35 ℃报警器温度过高日志记录温度 35 ℃七、这个模式最重要的地方注意TemperatureSensor完全不知道TemperatureDisplayTemperatureAlarmTemperatureLogger它只知道Observer也就是说TemperatureSensor │ │ 依赖抽象 ↓ Observer ↑ │ ┌─────┼────────┐ ↓ ↓ ↓ 显示器 报警器 日志这就是观察者模式最核心的思想面向抽象而不是面向具体观察者。八、如果用 Qt观察者模式就非常熟悉了其实你平时写 Qtconnect(button,QPushButton::clicked,this,MainWindow::onClicked);本质上就具有非常明显的观察者模式思想。例如QPushButton │ │ clicked ↓ ┌───┼───────────────┐ ↓ ↓ ↓ 窗口 日志系统 其他对象按钮是被观察者/事件发送者。其他对象是观察者。Qt 使用signal表示事件通知signals:void temperatureChanged(float temperature);然后connect(sensor,TemperatureSensor::temperatureChanged,display,TemperatureDisplay::update);这样温度变化的时候emit temperatureChanged(temperature);所有连接的对象都会收到通知。九、Qt 中写一个观察者模式实例例如温度传感器classTemperatureSensor:publicQObject{Q_OBJECTpublic:voidsetTemperature(floattemperature){if(qFuzzyCompare(m_temperature,temperature))return;m_temperaturetemperature;emittemperatureChanged(m_temperature);}signals:voidtemperatureChanged(floattemperature);private:floatm_temperature0;};观察者classTemperatureDisplay:publicQObject{Q_OBJECTpublicslots:voidonTemperatureChanged(floattemperature){qDebug()当前温度temperature;}};连接TemperatureSensor sensor;TemperatureDisplay display;QObject::connect(sensor,TemperatureSensor::temperatureChanged,display,TemperatureDisplay::onTemperatureChanged);然后sensor.setTemperature(25);就会自动执行display.onTemperatureChanged(25);十、观察者模式和发布-订阅的区别这两个概念非常容易混。观察者模式通常是Subject │ ├──── Observer1 ├──── Observer2 └──── Observer3Subject 直接持有 Observer。例如std::vectorObserver * m_observers;发布-订阅通常中间还有一个消息中心Publisher │ ↓ MessageBus │ ┌──┼────┐ ↓ ↓ ↓ Sub1 Sub2 Sub3Publisher 不直接知道 Subscriber。Qt 的 signal/slot、消息总线、事件总线等场景经常会出现这种思想。十一、观察者模式的优点① 解耦比如TemperatureSensor不需要知道TemperatureAlarm所以报警器可以随时增加、删除。② 支持一对多一个对象变化Sensor │ ┌──────┼──────┐ ↓ ↓ ↓ 显示器 报警器 日志可以通知很多对象。③ 扩展方便以后增加TemperatureCloudUploader只需要class TemperatureCloudUploader : public Observer{…};然后sensor.attach(cloudUploader);原来的 Sensor 不需要修改。这符合开闭原则。十二、观察者模式的缺点① 观察者太多如果Subject├── Observer1├── Observer2├── …└── Observer1000一次通知可能触发大量操作。② 通知链可能复杂比如A变化↓通知B↓B变化↓通知C↓C变化↓通知A甚至可能造成无限循环所以实际项目中需要注意通知关系。③ 生命周期问题传统 C 实现std::vectorObserver* m_observers;如果 Observer 被释放了delete observer;Subject 里面还保存着这个指针就可能出现野指针Qt 的信号槽在 QObject 生命周期管理方面就方便很多。十三、什么时候适合使用看到下面这种需求可以考虑观察者模式一个对象发生变化需要自动让多个对象知道。例如你现在做 Qt/C 项目很常见配置发生变化 ↓ ┌────┼────────┐ ↓ ↓ ↓ UI 日志 设备控制或者设备状态改变 ↓ ┌────┼─────────┐ ↓ ↓ ↓ 界面 数据记录 报警或者图像处理参数改变 ↓ ┌─────┼─────────┐ ↓ ↓ ↓ 图像 曲线 参数显示 刷新 刷新 刷新这种场景特别适合观察者模式。一句话记忆观察者模式 一个对象发生变化自动通知一群依赖它的对象。而在 Qt 中你可以把它直接对应成观察者模式↓Subject → signalsObserver → slotsnotify → emitattach → connect所以如果你正在学习设计模式并且主要用 Qt/C观察者模式是非常值得重点掌握的一个模式。