【C++设计模式】第二十三篇:观察者模式(Observer)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

事件驱动的订阅通知机制


1. 模式定义与用途

核心思想

  • 观察者模式 :定义一种一对多依赖关系 ,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)自动收到通知并更新
    • 关键用途
      1.解耦发布者与订阅者 :主题无需知道观察者的具体实现。
      2.实时通知 :支持动态添加或移除观察者。
      3.事件驱动架构:适用于GUI事件处理、数据监控、消息队列等场景。

经典场景

  • 股票价格变动通知多个交易终端。
  • 用户界面控件状态同步(如按钮点击触发面板更新)。
  • 传感器数据监控与报警系统。

2. 模式结构解析

UML类图

plaintext 复制代码
+---------------------+          +---------------------+  
|       Subject        |          |      Observer       |  
+---------------------+          +---------------------+  
| + attach(o: Observer)|          | + update(): void    |  
| + detach(o: Observer)|          +---------------------+  
| + notify()           |                    ^  
+---------------------+                    |  
          ^                                |  
          |                        +-------+-------+  
          |                        |               |  
+---------------------+    +-------------------+ +-------------------+  
|    ConcreteSubject  |    | ConcreteObserverA | | ConcreteObserverB |  
+---------------------+    +-------------------+ +-------------------+  
| + getState()        |    | + update()        | | + update()        |  
| + setState()        |    +-------------------+ +-------------------+  
+---------------------+  

角色说明

  1. Subject:主题接口,管理观察者列表,提供订阅/取消订阅方法。
  2. ConcreteSubject:具体主题,存储状态并在状态变化时通知观察者。
  3. Observer:观察者接口,定义更新方法update()
  4. ConcreteObserver:具体观察者,实现更新逻辑。

3. 现代C++实现示例

场景:股票价格变动通知系统

​步骤1:定义主题与观察者接口
cpp 复制代码
#include <iostream>  
#include <vector>  
#include <memory>  
#include <unordered_set>  

// 前向声明  
class StockObserver;  

// 主题接口  
class StockSubject {  
public:  
    virtual ~StockSubject() = default;  
    virtual void attach(std::shared_ptr<StockObserver> observer) = 0;  
    virtual void detach(std::shared_ptr<StockObserver> observer) = 0;  
    virtual void notify() = 0;  
};  

// 观察者接口  
class StockObserver : public std::enable_shared_from_this<StockObserver> {  
public:  
    virtual ~StockObserver() = default;  
    virtual void update(double price) = 0;  
};  
步骤2:实现具体主题(股票)
cpp 复制代码
class Stock : public StockSubject {  
public:  
    Stock(const std::string& symbol) : symbol_(symbol), price_(0.0) {}  

    void setPrice(double price) {  
        price_ = price;  
        notify();  
    }  

    void attach(std::shared_ptr<StockObserver> observer) override {  
        observers_.insert(observer);  
    }  

    void detach(std::shared_ptr<StockObserver> observer) override {  
        observers_.erase(observer);  
    }  

    void notify() override {  
        for (auto& observer : observers_) {  
            observer->update(price_);  
        }  
    }  

private:  
    std::string symbol_;  
    double price_;  
    std::unordered_set<std::shared_ptr<StockObserver>> observers_;  
};  
步骤3:实现具体观察者(交易终端)​
cpp 复制代码
class TradingTerminal : public StockObserver {  
public:  
    TradingTerminal(const std::string& name) : name_(name) {}  

    void update(double price) override {  
        std::cout << "终端 [" << name_ << "] 收到报价: $" << price << "\n";  
        if (price > alertThreshold_) {  
            std::cout << "警告:价格超过阈值!\n";  
        }  
    }  

    void setAlertThreshold(double threshold) {  
        alertThreshold_ = threshold;  
    }  

private:  
    std::string name_;  
    double alertThreshold_ = 100.0;  
};  
步骤4:客户端代码
cpp 复制代码
int main() {  
    // 创建股票与观察者  
    auto appleStock = std::make_shared<Stock>("AAPL");  
    auto terminal1 = std::make_shared<TradingTerminal>("终端1");  
    auto terminal2 = std::make_shared<TradingTerminal>("终端2");  
    terminal2->setAlertThreshold(150.0);  

    // 订阅股票价格  
    appleStock->attach(terminal1);  
    appleStock->attach(terminal2);  

    // 更新价格并触发通知  
    appleStock->setPrice(120.5);  
    /* 输出:  
        终端 [终端1] 收到报价: $120.5  
        警告:价格超过阈值!  
        终端 [终端2] 收到报价: $120.5  
    */  

    // 取消订阅终端2  
    appleStock->detach(terminal2);  
    appleStock->setPrice(160.0);  
    /* 输出:  
        终端 [终端1] 收到报价: $160  
        警告:价格超过阈值!  
    */  
}  

4. 应用场景示例

场景1:GUI事件处理

cpp 复制代码
class Button {  
public:  
    void addListener(std::shared_ptr<ButtonListener> listener) {  
        listeners_.push_back(listener);  
    }  

    void click() {  
        // 触发点击事件  
        for (auto& listener : listeners_) {  
            listener->onClick();  
        }  
    }  

private:  
    std::vector<std::shared_ptr<ButtonListener>> listeners_;  
};  

class LoggingListener : public ButtonListener {  
public:  
    void onClick() override {  
        std::cout << "按钮被点击,记录日志\n";  
    }  
};  

场景2:气象站数据监控

cpp 复制代码
class WeatherStation : public Subject {  
public:  
    void setTemperature(double temp) {  
        temperature_ = temp;  
        notify();  
    }  
};  

class PhoneApp : public Observer {  
public:  
    void update(double temp) override {  
        std::cout << "手机APP更新温度: " << temp << "°C\n";  
    }  
};  

5. 优缺点分析

​优点 ​缺点
松耦合设计,主题与观察者独立演化 观察者可能收到不相关通知(需过滤逻辑)
支持动态订阅机制 链式触发可能导致性能问题(级联更新)
简化事件分发逻辑 需注意内存泄漏(观察者未正确注销)

6. 调试与优化策略

调试技巧(VS2022)​

1. ​验证通知触发:
  • notify()方法中设置断点,检查观察者列表是否正确更新。
2. 跟踪观察者状态:
  • 使用​内存诊断工具确保观察者对象在注销后被正确释放。

​性能优化

1. 批量通知:
  • 合并多次状态变更,延迟发送通知(如防抖处理)。
cpp 复制代码
void Stock::setPrice(double price) {  
    price_ = price;  
    if (needNotify_) {  // 防抖逻辑  
        notify();  
        needNotify_ = false;  
    }  
}  
2. 异步通知:
cpp 复制代码
#include <future>  
void Stock::notify() {  
    std::async(std::launch::async, [this] {  
        for (auto& observer : observers_) {  
            observer->update(price_);  
        }  
    });  
}  
相关推荐
我想发发发13 分钟前
48. 旋转图像(C++)
开发语言·c++
绵绵细雨中的乡音1 小时前
动态规划-第2篇
c++·算法·动态规划
java技术小馆1 小时前
责任链模式如何减少模块之间的耦合
java·数据库·设计模式·责任链模式
0wioiw01 小时前
C++基础(VScode环境安装)
开发语言·c++
念故思旧1 小时前
【最长递增子序列】【LeetCode算法】【c++】【动态规划】
c++·算法·leetcode·动态规划
AI少女小鹿3 小时前
大一新生备战蓝桥杯c/c++B组——2024年省赛真题解题+心得分享
c语言·c++·蓝桥杯
恋恋风辰3 小时前
C++全栈聊天项目(2) 单例模式封装Http管理者
c++·http·单例模式
Vitalia3 小时前
⭐算法OJ⭐找到链表的中间节点【快慢指针】(C++实现)Middle of the Linked List
c++·算法·链表
轩宇^_^3 小时前
C++ 标准库:string 类、vector/List 容器与文件操作深度剖析
c++·list
seven97_top4 小时前
【设计模式】通过访问者模式实现分离算法与对象结构
设计模式·访问者模式