觀察者模式(Observer Pattern)是行為型設計模式中的事件通知專家,它定義了對象間一種一對多的依賴關系,當一個對象狀態改變時,所有依賴它的對象都會自動收到通知并更新。這種模式實現了發布-訂閱機制,是事件處理系統的核心基礎。本文將深入探索觀察者模式的核心思想、實現技巧以及在C++中的高效實踐。
為什么需要觀察者模式?
在軟件開發中,對象狀態變化需要通知其他對象:
GUI中的按鈕點擊事件處理
股票價格變動通知交易系統
游戲引擎中的碰撞檢測通知
分布式系統中的服務狀態更新
物聯網設備狀態同步
直接的對象間通知會導致:
緊耦合:對象間存在復雜依賴關系
維護困難:添加/刪除觀察者需修改主題代碼
性能問題:同步通知阻塞主線程
擴展性差:難以動態添加新觀察者類型
觀察者模式通過解耦主題和觀察者解決了這些問題。
觀察者模式的核心概念
模式結構解析
[主題] ← [具體主題]|| 通知▼
[觀察者] ← [具體觀察者]
關鍵角色定義
主題接口(Subject)
提供注冊、刪除和通知觀察者的接口
維護觀察者列表
具體主題(Concrete Subject)
實現主題接口
存儲狀態數據
狀態變化時通知觀察者
觀察者接口(Observer)
定義更新接口
接收主題通知
具體觀察者(Concrete Observer)
實現觀察者接口
維護對主題的引用
同步自身狀態與主題狀態
C++實現:股票交易通知系統
實現一個股票價格變動通知系統,展示觀察者模式的實際應用:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <algorithm>
#include <mutex>
#include <thread>
#include <chrono>
#include <random>// ================= 觀察者接口 =================
class StockObserver {
public:virtual ~StockObserver() = default;virtual void update(const std::string& symbol, double price) = 0;virtual std::string getName() const = 0;
};// ================= 主題接口 =================
class StockMarket {
public:virtual ~StockMarket() = default;virtual void registerObserver(const std::string& symbol, std::shared_ptr<StockObserver> observer) = 0;virtual void removeObserver(const std::string& symbol, const std::string& observerName) = 0;virtual void notifyObservers(const std::string& symbol) = 0;virtual void setPrice(const std::string& symbol, double price) = 0;virtual double getPrice(const std::string& symbol) const = 0;
};// ================= 具體主題:股票交易所 =================
class StockExchange : public StockMarket {
public:void registerObserver(const std::string& symbol, std::shared_ptr<StockObserver> observer) override {std::lock_guard lock(mutex_);observers_[symbol].push_back(observer);std::cout << observer->getName() << " 注冊監聽股票: " << symbol << std::endl;}void removeObserver(const std::string& symbol, const std::string& observerName) override {std::lock_guard lock(mutex_);auto& list = observers_[symbol];list.erase(std::remove_if(list.begin(), list.end(),[&](const auto& obs) {return obs->getName() == observerName;}),list.end());std::cout << observerName << " 取消監聽股票: " << symbol << std::endl;}void notifyObservers(const std::string& symbol) override {std::vector<std::shared_ptr<StockObserver>> currentObservers;{std::lock_guard lock(mutex_);if (observers_.find(symbol) == observers_.end()) return;currentObservers = observers_[symbol];}double currentPrice = getPrice(symbol);for (auto& observer : currentObservers) {observer->update(symbol, currentPrice);}}void setPrice(const std::string& symbol, double price) override {{std::lock_guard lock(mutex_);stockPrices_[symbol] = price;}std::cout << "\n=== " << symbol << " 價格更新: " << price << " ===" << std::endl;notifyObservers(symbol);}double getPrice(const std::string& symbol) const override {std::lock_guard lock(mutex_);auto it = stockPrices_.find(symbol);return it != stockPrices_.end() ? it->second : 0.0;}private:mutable std::mutex mutex_;std::unordered_map<std::string, double> stockPrices_;std::unordered_map<std::string, std::vector<std::shared_ptr<StockObserver>>> observers_;
};// ================= 具體觀察者:交易機器人 =================
class TradingBot : public StockObserver {
public:TradingBot(const std::string& name, double buyThreshold, double sellThreshold): name_(name), buyThreshold_(buyThreshold), sellThreshold_(sellThreshold) {}void update(const std::string& symbol, double price) override {std::cout << "🤖 " << name_ << " 收到 " << symbol << " 更新: " << price << std::endl;// 交易決策邏輯if (price <= buyThreshold_ && !holding_) {buy(symbol, price);} else if (price >= sellThreshold_ && holding_) {sell(symbol, price);}}std::string getName() const override { return name_; }private:void buy(const std::string& symbol, double price) {std::cout << "買入 " << symbol << " @ " << price << std::endl;holding_ = true;lastBuyPrice_ = price;}void sell(const std::string& symbol, double price) {double profit = price - lastBuyPrice_;std::cout << "賣出 " << symbol << " @ " << price << " | 利潤: " << profit << std::endl;holding_ = false;}std::string name_;double buyThreshold_;double sellThreshold_;bool holding_ = false;double lastBuyPrice_ = 0.0;
};// ================= 具體觀察者:價格警報器 =================
class PriceAlert : public StockObserver {
public:PriceAlert(const std::string& name, double alertPrice, bool above): name_(name), alertPrice_(alertPrice), above_(above) {}void update(const std::string& symbol, double price) override {if ((above_ && price >= alertPrice_) || (!above_ && price <= alertPrice_)) {std::cout << name_ << " 警報: " << symbol << " 價格 " << (above_ ? "突破" : "跌破") << " " << alertPrice_ << " (當前: " << price << ")" << std::endl;}}std::string getName() const override { return name_; }private:std::string name_;double alertPrice_;bool above_;
};// ================= 具體觀察者:移動應用通知 =================
class MobileApp : public StockObserver {
public:explicit MobileApp(const std::string& user) : user_(user) {}void update(const std::string& symbol, double price) override {std::cout << "[" << user_ << "] 股票更新: " << symbol << " 當前價格: " << price << std::endl;}std::string getName() const override { return "MobileApp-" + user_; }private:std::string user_;
};// ================= 股票價格模擬器 =================
class StockPriceSimulator {
public:StockPriceSimulator(StockMarket& market, const std::string& symbol, double startPrice, double volatility): market_(market), symbol_(symbol), currentPrice_(startPrice), volatility_(volatility), running_(false) {}void start() {running_ = true;thread_ = std::thread(&StockPriceSimulator::run, this);}void stop() {running_ = false;if (thread_.joinable()) thread_.join();}void run() {std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> dist(0.0, volatility_);while (running_) {// 隨機波動double change = dist(gen);currentPrice_ += currentPrice_ * change;// 確保價格不為負if (currentPrice_ < 0.1) currentPrice_ = 0.1;// 更新市場market_.setPrice(symbol_, currentPrice_);// 暫停std::this_thread::sleep_for(std::chrono::seconds(1));}}private:StockMarket& market_;std::string symbol_;double currentPrice_;double volatility_;bool running_;std::thread thread_;
};// ================= 客戶端代碼 =================
int main() {// 創建股票交易所auto exchange = std::make_shared<StockExchange>();// 初始化股票價格exchange->setPrice("AAPL", 150.0);exchange->setPrice("GOOGL", 2800.0);exchange->setPrice("TSLA", 700.0);// 創建觀察者auto bot1 = std::make_shared<TradingBot>("保守機器人", 145.0, 160.0);auto bot2 = std::make_shared<TradingBot>("激進機器人", 155.0, 170.0);auto alert1 = std::make_shared<PriceAlert>("AAPL警報", 160.0, true);auto alert2 = std::make_shared<PriceAlert>("TSLA警報", 750.0, true);auto mobileUser1 = std::make_shared<MobileApp>("張三");auto mobileUser2 = std::make_shared<MobileApp>("李四");// 注冊觀察者exchange->registerObserver("AAPL", bot1);exchange->registerObserver("AAPL", bot2);exchange->registerObserver("AAPL", alert1);exchange->registerObserver("AAPL", mobileUser1);exchange->registerObserver("GOOGL", mobileUser1);exchange->registerObserver("TSLA", alert2);exchange->registerObserver("TSLA", mobileUser2);// 啟動價格模擬器StockPriceSimulator aaplSimulator(*exchange, "AAPL", 150.0, 0.02);StockPriceSimulator tslaSimulator(*exchange, "TSLA", 700.0, 0.03);aaplSimulator.start();tslaSimulator.start();// 模擬運行一段時間std::this_thread::sleep_for(std::chrono::seconds(10));// 動態添加新觀察者std::cout << "\n===== 添加新觀察者 =====" << std::endl;auto newAlert = std::make_shared<PriceAlert>("AAPL下跌警報", 140.0, false);exchange->registerObserver("AAPL", newAlert);// 繼續運行std::this_thread::sleep_for(std::chrono::seconds(5));// 停止模擬aaplSimulator.stop();tslaSimulator.stop();// 移除部分觀察者std::cout << "\n===== 移除觀察者 =====" << std::endl;exchange->removeObserver("AAPL", "激進機器人");exchange->removeObserver("TSLA", "MobileApp-李四");return 0;
}
觀察者模式的五大優勢
解耦主題與觀察者
// 主題不知道觀察者具體類型 void notifyObservers() {for (auto& obs : observers_) {obs->update(data); // 僅依賴接口} }
動態添加觀察者
// 運行時注冊新觀察者 exchange->registerObserver("AAPL", newTradingBot);
支持廣播通信
// 通知所有訂閱者 void setPrice(const string& symbol, double price) {stockPrices_[symbol] = price;notifyObservers(symbol); // 廣播通知 }
事件驅動架構
// 響應狀態變化 void onPriceChange() {market_->setPrice(symbol_, newPrice); // 觸發事件 }
多播通信控制
// 按主題通知 void notifyObservers(const string& symbol) {// 只通知訂閱該symbol的觀察者 }
觀察者模式的高級應用
1. 事件總線系統
class EventBus {
public:void subscribe(const string& eventType, shared_ptr<Observer> observer) {subscribers_[eventType].push_back(observer);}void unsubscribe(const string& eventType, const string& observerName) {auto& list = subscribers_[eventType];list.erase(remove_if(list.begin(), list.end(),[&](auto& obs) { return obs->getName() == observerName; }), list.end());}void publish(const string& eventType, const string& data) {if (subscribers_.find(eventType) == subscribers_.end()) return;for (auto& observer : subscribers_[eventType]) {observer->handleEvent(eventType, data);}}
};
2. 異步觀察者
class AsyncNotifier {
public:void notify(shared_ptr<Observer> observer, const string& data) {queue_.push({observer, data});}void processNotifications() {while (!queue_.empty()) {auto [observer, data] = queue_.front();queue_.pop();observer->update(data);}}private:queue<pair<shared_ptr<Observer>, string>> queue_;
};class AsyncStockExchange : public StockExchange {void notifyObservers(const string& symbol) override {double price = getPrice(symbol);for (auto& observer : getObservers(symbol)) {asyncNotifier_.notify(observer, symbol, price);}}void processAsyncNotifications() {asyncNotifier_.processNotifications();}
};
3. 觀察者優先級
class PriorityStockExchange : public StockExchange {void registerObserver(const string& symbol, shared_ptr<StockObserver> observer,int priority) {auto& list = observers_[symbol];list.insert(std::upper_bound(list.begin(), list.end(), priority, [](int prio, const auto& obs) { return prio > obs->getPriority(); }), observer);}void notifyObservers(const string& symbol) override {auto observers = getObservers(symbol);for (auto& observer : observers) {observer->update(symbol, getPrice(symbol));}}
};
觀察者模式的應用場景
1. GUI事件處理
class Button {
public:void addClickListener(shared_ptr<ClickListener> listener) {clickListeners_.push_back(listener);}void click() {// UI邏輯...notifyClickListeners();}private:void notifyClickListeners() {for (auto& listener : clickListeners_) {listener->onClick(this);}}vector<shared_ptr<ClickListener>> clickListeners_;
};class LoginDialog {
public:LoginDialog() {loginButton_->addClickListener(make_shared<ClickListener>([this] {attemptLogin();}));}void attemptLogin() {// 登錄邏輯...}
};
2. 游戲引擎事件系統
class GameEventSystem {
public:void subscribe(EventType type, shared_ptr<GameObject> obj) {subscribers_[type].push_back(obj);}void publish(EventType type, EventData data) {for (auto& subscriber : subscribers_[type]) {subscriber->handleEvent(type, data);}}
};class Player : public GameObject {void handleEvent(EventType type, EventData data) override {if (type == EventType::COLLISION) {auto& collData = static_cast<CollisionData&>(data);if (collData.other->isEnemy()) {takeDamage(collData.damage);}}}
};class PhysicsEngine {void detectCollision() {// 碰撞檢測...eventSystem_.publish(EventType::COLLISION, collisionData);}
};
3. 配置變更通知
class ConfigManager {
public:void addChangeListener(shared_ptr<ConfigChangeListener> listener) {listeners_.push_back(listener);}void setValue(const string& key, const string& value) {config_[key] = value;notifyChangeListeners(key, value);}private:void notifyChangeListeners(const string& key, const string& value) {for (auto& listener : listeners_) {listener->onConfigChange(key, value);}}unordered_map<string, string> config_;vector<shared_ptr<ConfigChangeListener>> listeners_;
};class ThemeManager : public ConfigChangeListener {void onConfigChange(const string& key, const string& value) override {if (key == "theme") {applyTheme(value);}}
};
觀察者模式與其他模式的關系
模式 | 關系 | 區別 |
---|---|---|
中介者 | 都管理對象間通信 | 中介者集中控制,觀察者分布通知 |
發布-訂閱 | 觀察者的高級變體 | 發布-訂閱通常解耦更強 |
責任鏈 | 都處理請求 | 責任鏈傳遞請求,觀察者廣播通知 |
備忘錄 | 都可實現狀態通知 | 備忘錄保存狀態,觀察者響應狀態變化 |
組合使用示例:
// 觀察者 + 中介者
class ChatRoomMediator : public Mediator {void sendMessage(const string& msg, User* sender) override {for (auto user : users_) {if (user != sender) {user->receive(msg); // 觀察者通知}}}
};
觀察者模式的挑戰與解決方案
挑戰 | 解決方案 |
---|---|
觀察者執行阻塞 | 使用異步通知或線程池 |
通知順序依賴 | 實現優先級隊列 |
內存泄漏風險 | 使用弱引用或自動注銷機制 |
意外遞歸更新 | 實現更新標記和批處理 |
弱引用觀察者示例:
class WeakObserver : public StockObserver {
public:void registerWith(weak_ptr<StockMarket> market) {market_ = market;}void update(const string& symbol, double price) override {if (auto market = market_.lock()) {// 安全訪問}}private:weak_ptr<StockMarket> market_;
};
總結
觀察者模式是現代軟件架構的事件通信基石,它通過:
解耦設計:分離事件生產者和消費者
動態訂閱:運行時添加/移除觀察者
事件廣播:高效通知多個訂閱者
響應式編程:支持事件驅動架構
適用場景:
需要實現事件通知機制
需要解耦事件源和事件處理器
需要廣播狀態變化
需要動態管理訂閱關系
"觀察者模式不是簡單的回調機制,而是將事件處理提升為系統架構。它是響應式系統的通信生命線。" — 設計模式實踐者