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%