C++ 观察者模式
外观
观察者模式(Observer Pattern)是设计模式中一种行为型模式,用于在对象之间建立一对多的依赖关系,使得当一个对象(主题/被观察者)状态改变时,所有依赖它的对象(观察者)会自动收到通知并更新。该模式广泛应用于事件处理系统、GUI组件交互和实时数据推送等场景。
基本概念[编辑 | 编辑源代码]
观察者模式的核心角色包括:
- Subject(主题):维护一个观察者列表,提供添加、删除和通知观察者的接口。
- Observer(观察者):定义一个更新接口,用于接收主题状态变更的通知。
- ConcreteSubject(具体主题):实现主题的具体逻辑,存储状态并在状态变化时通知观察者。
- ConcreteObserver(具体观察者):实现观察者的更新逻辑,对主题状态变化做出响应。
模式结构[编辑 | 编辑源代码]
C++实现示例[编辑 | 编辑源代码]
以下是一个简单的C++实现,展示温度传感器(主题)与显示器(观察者)的交互:
#include <iostream>
#include <vector>
#include <algorithm>
// 前向声明
class Observer;
// 主题基类
class Subject {
public:
virtual void attach(Observer* o) = 0;
virtual void detach(Observer* o) = 0;
virtual void notify() = 0;
};
// 观察者基类
class Observer {
public:
virtual void update(float temperature) = 0;
};
// 具体主题:温度传感器
class TemperatureSensor : public Subject {
private:
std::vector<Observer*> observers;
float temperature;
public:
void attach(Observer* o) override {
observers.push_back(o);
}
void detach(Observer* o) override {
observers.erase(std::remove(observers.begin(), observers.end(), o), observers.end());
}
void notify() override {
for (auto* o : observers) {
o->update(temperature);
}
}
void setTemperature(float temp) {
temperature = temp;
notify(); // 状态变化时通知观察者
}
};
// 具体观察者:温度显示器
class TemperatureDisplay : public Observer {
public:
void update(float temperature) override {
std::cout << "Temperature updated: " << temperature << "°C\n";
}
};
int main() {
TemperatureSensor sensor;
TemperatureDisplay display;
sensor.attach(&display);
sensor.setTemperature(23.5); // 输出:Temperature updated: 23.5°C
sensor.setTemperature(25.0); // 输出:Temperature updated: 25°C
return 0;
}
代码解释[编辑 | 编辑源代码]
1. Subject 和 Observer 是抽象基类,定义接口规范。 2. TemperatureSensor 是具体主题,维护观察者列表并在温度变化时调用 `notify()`。 3. TemperatureDisplay 是具体观察者,实现 `update()` 方法以响应状态变化。 4. 在 `main()` 中,传感器与显示器通过观察者模式解耦,传感器无需直接调用显示器的接口。
实际应用场景[编辑 | 编辑源代码]
观察者模式在以下场景中非常有用:
- GUI事件处理:如按钮点击事件通知多个监听器。
- 发布-订阅系统:如消息队列中的生产者-消费者模型。
- 游戏开发:角色状态变化触发UI或AI的更新。
案例:股票价格通知[编辑 | 编辑源代码]
假设有一个股票交易系统,当股票价格变化时,需要通知所有订阅该股票的投资者:
// 省略基类定义(与温度示例类似)
class Stock : public Subject {
private:
std::string symbol;
double price;
public:
Stock(const std::string& s, double p) : symbol(s), price(p) {}
void setPrice(double p) {
price = p;
notify();
}
std::string getSymbol() const { return symbol; }
double getPrice() const { return price; }
};
class Investor : public Observer {
private:
std::string name;
public:
Investor(const std::string& n) : name(n) {}
void update(double price) override {
std::cout << name << " notified: Price changed to $" << price << "\n";
}
};
// 使用示例
int main() {
Stock apple("AAPL", 150.0);
Investor alice("Alice"), bob("Bob");
apple.attach(&alice);
apple.attach(&bob);
apple.setPrice(155.5); // 输出两条通知
}
进阶讨论[编辑 | 编辑源代码]
推模型 vs 拉模型[编辑 | 编辑源代码]
- 推模型:主题将详细数据通过 `update()` 参数传递给观察者(如上述示例)。
- 拉模型:观察者通过主题的接口主动获取数据(需在 `update()` 中调用 `getState()`)。
线程安全问题[编辑 | 编辑源代码]
在多线程环境中,需对观察者列表的修改和遍历加锁(如使用 `std::mutex`)。
总结[编辑 | 编辑源代码]
观察者模式通过解耦主题和观察者,增强了代码的灵活性和可维护性。C++中可通过继承和纯虚函数实现该模式,适用于需要动态响应用户交互或数据变化的系统。