C++设计模式之观察者模式

文章目录


前言

本文主要探讨C++设计模式之观察者模式。


一、观察者模式简介

观察者模式(Observer Pattern)是一种行为型设计模式,主要用于实现对象之间的一对多关系。当一个对象的状态发生变化时,所有依赖于它的对象都自动收到通知并进行更新。这种模式常用于实现事件驱动系统,例如GUI组件、数据变化通知、实时更新等场景。

二、观察者模式的组成

观察者模式主要由以下几个角色组成:

  • 主题(Subject):被观察的对象,维护观察者的列表,并在自身状态变化时通知所有观察者。
  • 观察者(Observer):需要关注主题状态变化的对象,定义一个更新方法,当主题状态改变时被调用。
  • 具体主题(Concrete Subject):实现主题接口,存储观察者和自身的状态。当状态变化时,调用观察者的更新方法。
  • 具体观察者(Concrete Observer):实现观察者接口,具体处理主题的状态变化。

三、工作原理

观察者模式的工作流程如下:

  • 观察者注册到主题中,主题维护一个观察者列表。
  • 当主题的状态发生变化时,主题会调用所有注册观察者的更新方法。
  • 观察者根据主题的最新状态进行相应的处理。

四、应用示例

下面是一个完整的观察者模式实现示例,以天气监测系统为例。

1. 定义观察者和主题接口

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

// 观察者接口,所有观察者都必须实现这个接口
class Observer {
public:
    virtual void update(float temperature, float humidity) = 0;
    virtual ~Observer() = default;
};

// 主题接口,定义了观察者注册、注销和通知的方法
class Subject {
public:
    virtual void attach(std::shared_ptr<Observer> observer) = 0;
    virtual void detach(std::shared_ptr<Observer> observer) = 0;
    virtual void notify() = 0;
    virtual ~Subject() = default;
};

2. 实现具体主题类

cpp 复制代码
// WeatherData 类实现了主题接口,维护观察者的列表并通知它们
class WeatherData : public Subject {
private:
    std::vector<std::shared_ptr<Observer>> m_observers;// 存储观察者的列表
    float m_temperature;// 存储温度
    float m_humidity;// 存储湿度

public:
    // 添加观察者到列表中
    void attach(std::shared_ptr<Observer> observer) override {
        // 检查 observer 是否有效且不在列表中
        if (observer && std::find(m_observers.begin(), m_observers.end(), observer) == m_observers.end()) {
            m_observers.push_back(observer);
        }
    }
    // 从列表中移除观察者
    void detach(std::shared_ptr<Observer> observer) override {
        m_observers.erase(std::remove(m_observers.begin(), m_observers.end(), observer), m_observers.end());
    }
    // 通知所有观察者更新数据
    void notify() override {
        for (auto& observer : m_observers) {
            observer->update(m_temperature, m_humidity);
        }
    }
    // 设置新的天气数据并通知所有观察者
    void setMeasurements(const float temp, const float hum) {
        m_temperature = temp;
        m_humidity = hum;
        notify();
    }
};

3. 实现具体观察者类

cpp 复制代码
// 当前天气显示类,实现了观察者接口
class CurrentConditionsDisplay : public Observer {
private:
    float m_temperature;
    float m_humidity;

public:
    void update(float temp, float hum) override {
        m_temperature = temp;
        m_humidity = hum;
        display();
    }

    void display() {
        std::cout << "Current conditions: " << m_temperature << "°C and " << m_humidity << "% humidity." << std::endl;
    }
};

// 统计显示类,实现了观察者接口,计算和显示平均天气数据
class StatisticsDisplay : public Observer {
private:
    float m_temperatureSum;
    float m_humiditySum;
    int m_numReadings;

public:
    StatisticsDisplay() : m_temperatureSum(0), m_humiditySum(0), m_numReadings(0) {}

    void update(float temp, float hum) override {
        m_temperatureSum += temp;
        m_humiditySum += hum;
        m_numReadings++;
        display();
    }

    void display() {
        std::cout << "Average temperature: " << (m_temperatureSum / m_numReadings) 
                  << "°C, Average humidity: " << (m_humiditySum / m_numReadings) << "%" << std::endl;
    }
};

4. 使用观察者模式

cpp 复制代码
int main() 
{
    // 创建主题实例
    WeatherData weatherData;

    // 创建观察者实例,使用智能指针管理内存
    auto currentDisplay = std::make_shared<CurrentConditionsDisplay>();
    auto statisticsDisplay = std::make_shared<StatisticsDisplay>();

    // 注册观察者
    weatherData.attach(currentDisplay);
    weatherData.attach(statisticsDisplay);

    // 设置天气数据,通知所有观察者
    weatherData.setMeasurements(30.0f, 65.0f);
    weatherData.setMeasurements(28.0f, 70.0f);
    weatherData.setMeasurements(26.0f, 80.0f);

    // 注销观察者
    weatherData.detach(currentDisplay);
    weatherData.detach(statisticsDisplay);

    return 0;
}

编译输出如下:

bash 复制代码
jeff@jeff:~/jeffPro/practice/DesignPattern$ ./design 
Current conditions: 30°C and 65% humidity.
Average temperature: 30°C, Average humidity: 65%
Current conditions: 28°C and 70% humidity.
Average temperature: 29°C, Average humidity: 67.5%
Current conditions: 26°C and 80% humidity.
Average temperature: 28°C, Average humidity: 71.6667%
jeff@jeff:~/jeffPro/practice/DesignPattern$ 

五、优缺点以及适用场景

以下是观察者模式的优缺点以及适用场景:

|----------|---------------------------------|
| 属性 | 具体描述 |
| 优势 |
| 优势 | 松耦合:主题和观察者之间的依赖关系较弱,便于独立修改和扩展。 |
| 优势 | 动态扩展性:运行时可以灵活添加或移除观察者,增强系统的灵活性。 |
| 优势 | 广泛适用性:适用于事件驱动的应用场景,如GUI和实时数据更新。 |
| 劣势 |
| 劣势 | 性能影响:大量观察者时,通知所有观察者可能导致性能下降。 |
| 劣势 | 通知泛滥:频繁的状态更新可能使观察者接收过多不必要的通知。 |
| 劣势 | 更新顺序不确定:观察者的通知顺序不固定,可能影响系统行为。 |
| 适用场景 |
| 适用场景 | 用户界面:组件状态变化时,其他相关组件需要自动更新。 |
| 适用场景 | 实时数据监控:如股票价格、天气变化等,需要即时通知用户。 |
| 适用场景 | 事件驱动系统:例如在游戏开发中,处理事件发生和响应。 |

相关推荐
Death20031 分钟前
PCL库中的算法封装详解
c++·qt·算法·3d
勇敢滴勇32 分钟前
【C++】继承和多态常见的面试问题
java·c++·面试
Qhumaing41 分钟前
小白C/C++语言常见问题(持续更新)
c语言·c++·算法
小柯J桑_1 小时前
C++:二叉搜索树(迭代)
c++·二叉搜索树
铭正1 小时前
c++时间形式转换
c++·时间格式转换
flying robot1 小时前
编程语言的设计模式
设计模式
啦啦啦!1 小时前
2024年10月30日(双指针算法)
c++·算法·leetcode
△曉風殘月〆2 小时前
C#与C++结构体的交互
c++·c#·p/invoke
hugerat2 小时前
嵌入式linux跨平台基于mongoose的TCP C++类的源码
linux·c++·tcp/ip
无敌岩雀2 小时前
C++设计模式创建型模式———单例模式
c++·单例模式·设计模式