观察者模式 (Observer Pattern)与几个C++应用例子
1. 模式定义与核心思想
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,它会自动通知所有观察者对象,使它们能够自动更新自己。
核心思想: 解耦主题和观察者。主题不需要知道哪些具体对象在观察它,它只需要维护一个观察者列表,并在状态改变时向它们发送通知。这使得系统更灵活,可以动态地添加和删除观察者,而无需修改主题的代码。
2. 模式结构(角色分析)
观察者模式通常包含以下四个角色:
Subject (主题 / 被观察者):
维护一个观察者(Observer)对象的集合。
提供接口可以添加(Attach)和删除(Detach)观察者。
提供接口用于通知(Notify)所有注册的观察者。
ConcreteSubject (具体主题):
继承自 Subject。
维护其自身的状态(例如,一个温度值、一个鼠标点击位置)。
当状态发生改变时,调用父类的 Notify 方法通知所有观察者。
Observer (观察者):
为所有具体观察者定义一个更新接口。通常是一个抽象的 Update() 方法。
当接到主题的通知时,调用此方法做出响应。
ConcreteObserver (具体观察者):
继承自 Observer。
实现 Update() 方法。在此方法中,通常会从 ConcreteSubject 获取所需的状态,并执行具体的业务逻辑(如更新UI、记录日志等)。
通常会维护一个指向 ConcreteSubject 的引用,以便在更新时获取其状态。
它们之间的协作关系如下图所示:
(这是一个UML协作序列的文本描述)
ConcreteObserver 调用 ConcreteSubject 的 Attach 方法将自己注册到主题的观察者列表中。
ConcreteSubject 的内部状态发生变化。
ConcreteSubject 调用 Notify 方法。
Notify 方法遍历所有注册的 Observer,并调用每个观察者的 Update 方法。
ConcreteObserver 的 Update 方法被调用,它可以通过传入的参数或查询 ConcreteSubject 来获取新状态,并据此更新自身。
3. 经典C++实现示例
下面是一个简单的C++实现,模拟一个气象站(主题)和多个显示设备(观察者)。
#include <iostream>
#include <vector>
#include <string>
#include <memory>// 前向声明
class Subject;// 1. Observer (观察者接口)
class Observer {
public:virtual ~Observer() = default;// 更新接口,参数通常是Subject或状态数据virtual void Update(Subject& subject) = 0;
};// 2. Subject (主题接口)
class Subject {
public:virtual ~Subject() = default;// 注册观察者void Attach(std::shared_ptr<Observer> observer) {observers_.push_back(observer);}// 移除观察者void Detach(std::shared_ptr<Observer> observer) {// 在实际项目中,这里需要更安全的删除逻辑observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),observers_.end());}// 通知所有观察者void Notify() {for (auto& observer : observers_) {observer->Update(*this);}}private:std::vector<std::shared_ptr<Observer>> observers_;
};// 3. ConcreteSubject (具体主题:气象站)
class WeatherStation : public Subject {
public:// 设置状态(温度)并通知观察者void SetTemperature(double temp) {temperature_ = temp;Notify(); // 关键一步:状态改变,立即通知所有观察者}// 获取状态(供观察者查询)double GetTemperature() const {return temperature_;}private:double temperature_ = 0.0;
};// 4. ConcreteObserver (具体观察者:手机显示)
class PhoneDisplay : public Observer {
public:explicit PhoneDisplay(const std::string& name) : name_(name) {}void Update(Subject& subject) override {// 安全的向下转型,确认主题类型WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);if (ws) {double temp = ws->GetTemperature();std::cout << "[" << name_ << "] Temperature updated: " << temp << "°C\n";}}private:std::string name_;
};// 5. 另一个具体观察者:LED大屏显示
class LedDisplay : public Observer {
public:void Update(Subject& subject) override {WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);if (ws) {double temp = ws->GetTemperature();std::cout << "*** LED Display: CURRENT TEMP = " << temp << "°C ***\n";}}
};// 客户端代码
int main() {// 创建主题(气象站)WeatherStation station;// 创建观察者(两个显示设备)auto phone1 = std::make_shared<PhoneDisplay>("User's Phone");auto phone2 = std::make_shared<PhoneDisplay>("Dad's Phone");auto led = std::make_shared<LedDisplay>();// 注册观察者station.Attach(phone1);station.Attach(phone2);station.Attach(led);// 模拟气象站温度变化,观察者会自动更新std::cout << "Setting temperature to 25.5°C...\n";station.SetTemperature(25.5);std::cout << "\nSetting temperature to 18.2°C...\n";station.SetTemperature(18.2);// 移除一个观察者std::cout << "\nDetaching Dad's Phone...\n";station.Detach(phone2);std::cout << "Setting temperature to 30.0°C...\n";station.SetTemperature(30.0);return 0;
}
输出结果:
Setting temperature to 25.5°C...
[User's Phone] Temperature updated: 25.5°C
[Dad's Phone] Temperature updated: 25.5°C
*** LED Display: CURRENT TEMP = 25.5°C ***Setting temperature to 18.2°C...
[User's Phone] Temperature updated: 18.2°C
[Dad's Phone] Temperature updated: 18.2°C
*** LED Display: CURRENT TEMP = 18.2°C ***Detaching Dad's Phone...
Setting temperature to 30.0°C...
[User's Phone] Temperature updated: 30°C
*** LED Display: CURRENT TEMP = 30°C ***
更多例子:
示例 1: GUI 事件处理 (按钮点击)
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>// 前向声明
class Button;// 观察者接口:事件监听器
class EventListener {
public:virtual ~EventListener() = default;virtual void onClick(Button& source) = 0;
};// 主题:按钮
class Button {std::vector<EventListener*> listeners_; // 使用原始指针简化示例std::string name_;public:Button(const std::string& name) : name_(name) {}// 注册观察者void addListener(EventListener* listener) {listeners_.push_back(listener);}// 移除观察者void removeListener(EventListener* listener) {listeners_.erase(std::remove(listeners_.begin(), listeners_.end(), listener), listeners_.end());}// 模拟用户点击void click() {std::cout << "Button '" << name_ << "' was clicked!\n";notifyListeners();}const std::string& getName() const { return name_; }private:// 通知所有观察者void notifyListeners() {for (auto listener : listeners_) {listener->onClick(*this);}}
};// 具体观察者:登录处理器
class LoginHandler : public EventListener {
public:void onClick(Button& source) override {std::cout << "[LoginHandler] Handling click from: " << source.getName() << "\n";std::cout << "Performing login logic...\n";}
};// 具体观察者:日志记录器
class ClickLogger : public EventListener {
public:void onClick(Button& source) override {std::cout << "[ClickLogger] Button clicked: " << source.getName() << " at timestamp: 12345\n";}
};int main() {Button loginButton("Login");LoginHandler loginHandler;ClickLogger logger;// 注册监听器loginButton.addListener(&loginHandler);loginButton.addListener(&logger);// 模拟点击事件loginButton.click();std::cout << "\nRemoving logger...\n";loginButton.removeListener(&logger);loginButton.click();return 0;
}
输出结果:
Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...
[ClickLogger] Button clicked: Login at timestamp: 12345Removing logger...
Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...
示例 2: 发布-订阅系统 (简单的消息主题)
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>// 消息类
struct Message {std::string topic;std::string content;
};// 订阅者接口
class Subscriber {
public:virtual ~Subscriber() = default;virtual void onMessage(const Message& msg) = 0;
};// 消息代理(主题)
class MessageBroker {std::map<std::string, std::vector<Subscriber*>> topicSubscribers_;public:// 订阅主题void subscribe(const std::string& topic, Subscriber* sub) {topicSubscribers_[topic].push_back(sub);}// 取消订阅void unsubscribe(const std::string& topic, Subscriber* sub) {auto& subs = topicSubscribers_[topic];subs.erase(std::remove(subs.begin(), subs.end(), sub), subs.end());}// 发布消息void publish(const Message& msg) {auto it = topicSubscribers_.find(msg.topic);if (it != topicSubscribers_.end()) {for (auto sub : it->second) {sub->onMessage(msg);}}}
};// 具体订阅者:日志服务
class LogService : public Subscriber {
public:void onMessage(const Message& msg) override {std::cout << "[LogService] Received on topic '" << msg.topic << "': " << msg.content << "\n";}
};// 具体订阅者:告警服务
class AlertService : public Subscriber {
public:void onMessage(const Message& msg) override {if (msg.topic == "alerts") {std::cout << "[AlertService] ALERT! " << msg.content << "\n";}}
};int main() {MessageBroker broker;LogService logger;AlertService alerter;// 订阅主题broker.subscribe("logs", &logger);broker.subscribe("alerts", &logger);broker.subscribe("alerts", &alerter);// 发布消息broker.publish({"logs", "System started successfully"});broker.publish({"alerts", "CPU usage over 90%"});broker.publish({"metrics", "This will be ignored by all"}); // 无订阅者return 0;
}
输出结果:
[LogService] Received on topic 'logs': System started successfully
[LogService] Received on topic 'alerts': CPU usage over 90%
[AlertService] ALERT! CPU usage over 90%