【C++】观察者模式

目录

观察者模式(Observer Pattern)是一种【行为型】设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。这种模式将发布者(被观察对象)与订阅者(观察者)解耦,使它们可以独立地变化和复用。

一、模式核心概念与结构

观察者模式包含四个核心角色:

  1. 主题接口(Subject):定义添加、删除和通知观察者的接口。
  2. 具体主题(Concrete Subject):维护观察者列表,状态变化时通知所有观察者。
  3. 观察者接口(Observer):定义接收通知的更新接口。
  4. 具体观察者(Concrete Observer):实现更新接口,处理来自主题的通知。

二、C++ 实现示例:股票价格监控系统

以下是一个简单的股票价格监控系统示例,演示如何使用观察者模式实现价格变化通知:

cpp 复制代码
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <algorithm>

// 前向声明
class Observer;

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

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(double price) = 0;
};

// 具体主题:股票
class Stock : public Subject {
private:
    std::string symbol;
    double price;
    std::vector<std::shared_ptr<Observer>> observers;

public:
    Stock(const std::string& sym, double p) : symbol(sym), price(p) {}
    
    void attach(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
        std::cout << "Observer attached: " << symbol << std::endl;
    }
    
    void detach(std::shared_ptr<Observer> observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
        std::cout << "Observer detached: " << symbol << std::endl;
    }
    
    void notify() override {
        std::cout << "Notifying observers about price change in " << symbol << std::endl;
        for (auto& observer : observers) {
            observer->update(price);
        }
    }
    
    void setPrice(double newPrice) {
        if (price != newPrice) {
            price = newPrice;
            notify();
        }
    }
    
    double getPrice() const {
        return price;
    }
    
    std::string getSymbol() const {
        return symbol;
    }
};

// 具体观察者:股票投资者
class Investor : public Observer {
private:
    std::string name;

public:
    Investor(const std::string& n) : name(n) {}
    
    void update(double price) override {
        std::cout << name << " received price update: " << price << std::endl;
    }
};

// 客户端代码
int main() {
    // 创建股票(主题)
    auto appleStock = std::make_shared<Stock>("AAPL", 150.0);
    auto googleStock = std::make_shared<Stock>("GOOGL", 2000.0);
    
    // 创建投资者(观察者)
    auto investor1 = std::make_shared<Investor>("Alice");
    auto investor2 = std::make_shared<Investor>("Bob");
    
    // 注册观察者到主题
    appleStock->attach(investor1);
    appleStock->attach(investor2);
    googleStock->attach(investor1);
    
    // 股票价格变化
    appleStock->setPrice(155.5);
    googleStock->setPrice(2010.25);
    
    // 投资者2不再关注苹果股票
    appleStock->detach(investor2);
    
    // 苹果股票再次变化
    appleStock->setPrice(160.0);
    
    return 0;
}

三、观察者模式的关键特性

  1. 松耦合
    • 主题和观察者之间仅通过抽象接口交互,降低了耦合度。
  2. 一对多依赖
    • 一个主题可以有多个观察者,当主题状态变化时,所有观察者都会被通知。
  3. 广播通知
    • 主题发送通知时不需要知道观察者的具体类型,简化了系统设计。
  4. 动态关系
    • 运行时可以动态添加或删除观察者,提高了系统灵活性。

四、应用场景

  1. 事件系统
    • GUI 组件的事件处理(如按钮点击、窗口关闭)。
    • 例如,Qt 框架中的信号与槽机制。
  2. 状态监控
    • 系统状态变化时通知相关组件(如传感器数据更新、股票价格变化)。
  3. 发布 - 订阅系统
    • 消息队列、实时数据流处理(如 Redis 发布订阅、Kafka)。
  4. MVC 架构
    • 模型(Model)作为主题,视图(View)作为观察者,实现数据与显示的分离。

五、观察者模式与其他设计模式的关系

  1. 中介者模式
    • 中介者模式可以封装观察者和主题之间的交互,使它们不需要直接引用对方。
  2. 单例模式
    • 主题可以实现为单例,确保系统中只有一个实例被观察。
  3. 责任链模式
    • 观察者可以形成责任链,按顺序处理通知。

六、C++ 标准库中的观察者模式应用

  1. 信号与槽
    • Qt 框架中的信号与槽是观察者模式的典型实现。
  2. STL 中的容器和算法
    • 容器状态变化时可以通知关联的迭代器或算法。
  3. 智能指针
    • std::weak_ptr可以观察std::shared_ptr的生命周期,避免悬空指针。

七、优缺点分析

优点:

  • 松耦合:主题和观察者解耦,便于独立修改和扩展。
  • 可复用性:主题和观察者可以独立复用。
  • 动态关系:运行时可以动态添加或删除观察者。
  • 符合开闭原则:新增观察者无需修改主题代码。

缺点:

  • 内存管理风险:如果观察者没有正确注销,可能导致内存泄漏。
  • 通知顺序不确定:多个观察者的通知顺序可能不可预测。
  • 性能问题:大量观察者可能导致通知开销较大。

八、实战案例:气象站监控系统

以下是一个气象站监控系统的实现示例,演示如何使用观察者模式实现气象数据的实时更新:

cpp 复制代码
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <algorithm>

// 前向声明
class WeatherObserver;

// 主题接口
class WeatherSubject {
public:
    virtual ~WeatherSubject() = default;
    virtual void registerObserver(std::shared_ptr<WeatherObserver> observer) = 0;
    virtual void removeObserver(std::shared_ptr<WeatherObserver> observer) = 0;
    virtual void notifyObservers() = 0;
};

// 观察者接口
class WeatherObserver {
public:
    virtual ~WeatherObserver() = default;
    virtual void update(double temperature, double humidity, double pressure) = 0;
};

// 具体主题:气象站
class WeatherStation : public WeatherSubject {
private:
    double temperature;
    double humidity;
    double pressure;
    std::vector<std::shared_ptr<WeatherObserver>> observers;

public:
    void registerObserver(std::shared_ptr<WeatherObserver> observer) override {
        observers.push_back(observer);
        std::cout << "Observer registered" << std::endl;
    }
    
    void removeObserver(std::shared_ptr<WeatherObserver> observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
        std::cout << "Observer removed" << std::endl;
    }
    
    void notifyObservers() override {
        std::cout << "Notifying observers about weather update" << std::endl;
        for (auto& observer : observers) {
            observer->update(temperature, humidity, pressure);
        }
    }
    
    void measurementsChanged() {
        notifyObservers();
    }
    
    void setMeasurements(double temp, double hum, double pres) {
        temperature = temp;
        humidity = hum;
        pressure = pres;
        measurementsChanged();
    }
};

// 具体观察者:当前状况显示
class CurrentConditionsDisplay : public WeatherObserver {
public:
    void update(double temperature, double humidity, double pressure) override {
        std::cout << "Current conditions: " << temperature 
                  << "°C and " << humidity << "% humidity" << std::endl;
    }
};

// 具体观察者:天气预报显示
class ForecastDisplay : public WeatherObserver {
private:
    double lastPressure;

public:
    ForecastDisplay() : lastPressure(0) {}
    
    void update(double temperature, double humidity, double pressure) override {
        std::cout << "Forecast: ";
        if (pressure > lastPressure) {
            std::cout << "Improving weather on the way!" << std::endl;
        } else if (pressure == lastPressure) {
            std::cout << "More of the same" << std::endl;
        } else {
            std::cout << "Watch out for cooler, rainy weather" << std::endl;
        }
        lastPressure = pressure;
    }
};

// 客户端代码
int main() {
    // 创建气象站(主题)
    auto weatherStation = std::make_shared<WeatherStation>();
    
    // 创建显示设备(观察者)
    auto currentDisplay = std::make_shared<CurrentConditionsDisplay>();
    auto forecastDisplay = std::make_shared<ForecastDisplay>();
    
    // 注册观察者
    weatherStation->registerObserver(currentDisplay);
    weatherStation->registerObserver(forecastDisplay);
    
    // 更新气象数据
    weatherStation->setMeasurements(25.0, 65.0, 1013.2);
    
    std::cout << "\n";
    
    // 再次更新
    weatherStation->setMeasurements(23.0, 70.0, 1009.8);
    
    // 移除一个观察者
    weatherStation->removeObserver(forecastDisplay);
    
    std::cout << "\n";
    
    // 再次更新
    weatherStation->setMeasurements(20.0, 75.0, 1005.3);
    
    return 0;
}

九、实现注意事项

  1. 内存管理
    • 确保观察者在不再需要时正确注销,避免内存泄漏。可以使用弱引用(如std::weak_ptr)。
  2. 线程安全
    • 在多线程环境中,通知过程可能需要同步机制(如互斥锁)。
  3. 通知顺序
    • 如果观察者执行顺序很重要,需确保主题按特定顺序通知观察者。
  4. 状态快照
    • 通知时可以传递状态快照而非引用,避免观察者修改主题状态。

观察者模式是 C++ 中实现对象间动态依赖关系的重要工具,通过将发布者与订阅者解耦,使系统更具灵活性和可扩展性,特别适合需要实时数据更新和事件驱动的应用场景。


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

相关推荐
CoderCodingNo9 分钟前
【GESP】C++四级考试大纲知识点梳理, (7) 排序算法基本概念
开发语言·c++·排序算法
秋风&萧瑟2 小时前
【C++】C++中的友元函数和友元类
c++
梁诚斌2 小时前
使用OpenSSL接口读取pem编码格式文件中的证书
开发语言·c++
缘来是庄3 小时前
设计模式之建造者模式
java·设计模式·建造者模式
铛铛啦啦啦6 小时前
“对象创建”模式之原型模式
设计模式·原型模式
2301_803554526 小时前
c++中的绑定器
开发语言·c++·算法
海棠蚀omo6 小时前
C++笔记-位图和布隆过滤器
开发语言·c++·笔记
牛奶咖啡137 小时前
学习设计模式《十六》——策略模式
学习·设计模式·策略模式·认识策略模式·策略模式的优缺点·何时选用策略模式·策略模式的使用示例
消失的旧时光-19437 小时前
c++ 的标准库 --- std::
c++·jni
GiraKoo7 小时前
【GiraKoo】C++11的新特性
c++·后端