观察者模式(Observer Pattern)

观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象同时监听某一个主题对象。当该主题对象的状态发生变化时,它会通知所有观察者对象,使它们能够自动更新。

观察者模式的主要特点包括:

  1. 解耦:观察者模式将观察者与被观察者解耦,使得它们可以独立变化。
  2. 动态订阅:观察者可以在运行时动态地订阅或取消订阅被观察者的通知。
  3. 广播通信:被观察者向所有注册的观察者广播通知,而不需要知道具体有哪些观察者。

示例:使用观察者模式实现一个简单的天气预报系统

我们将实现一个简单的天气预报系统,包含一个天气数据的主题和两个观察者:一个显示当前天气情况,另一个显示天气统计信息。

接口和具体类

首先,定义观察者和主题的接口:

#include <iostream>
#include <vector>
#include <memory>

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

// 主题接口
class Subject {
public:
    virtual ~Subject() = default;
    virtual void registerObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void removeObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void notifyObservers() = 0;
};
具体主题类

然后,创建具体的主题类:

class WeatherData : public Subject {
public:
    void registerObserver(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
    }

    void removeObserver(std::shared_ptr<Observer> observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers() override {
        for (const auto& observer : observers) {
            observer->update(temperature, humidity, pressure);
        }
    }

    void setMeasurements(float temp, float hum, float pres) {
        temperature = temp;
        humidity = hum;
        pressure = pres;
        notifyObservers();
    }

private:
    std::vector<std::shared_ptr<Observer>> observers;
    float temperature;
    float humidity;
    float pressure;
};
具体观察者类

接着,创建具体的观察者类:

class CurrentConditionsDisplay : public Observer {
public:
    void update(float temperature, float humidity, float pressure) override {
        this->temperature = temperature;
        this->humidity = humidity;
        display();
    }

    void display() {
        std::cout << "Current conditions: " << temperature
                  << "C degrees and " << humidity << "% humidity" << std::endl;
    }

private:
    float temperature;
    float humidity;
};

class StatisticsDisplay : public Observer {
public:
    void update(float temperature, float humidity, float pressure) override {
        tempSum += temperature;
        numReadings++;
        if (temperature > maxTemp) {
            maxTemp = temperature;
        }
        if (temperature < minTemp) {
            minTemp = temperature;
        }
        display();
    }

    void display() {
        std::cout << "Avg/Max/Min temperature = " << (tempSum / numReadings)
                  << "/" << maxTemp << "/" << minTemp << std::endl;
    }

private:
    float maxTemp = 0.0f;
    float minTemp = 200.0f;
    float tempSum = 0.0f;
    int numReadings = 0;
};
主函数测试

最后,在主函数中创建主题和观察者对象,并模拟天气数据的变化:

int main() {
    std::shared_ptr<WeatherData> weatherData = std::make_shared<WeatherData>();

    std::shared_ptr<CurrentConditionsDisplay> currentDisplay = std::make_shared<CurrentConditionsDisplay>();
    std::shared_ptr<StatisticsDisplay> statisticsDisplay = std::make_shared<StatisticsDisplay>();

    weatherData->registerObserver(currentDisplay);
    weatherData->registerObserver(statisticsDisplay);

    weatherData->setMeasurements(25.0f, 65.0f, 1013.0f);
    weatherData->setMeasurements(27.0f, 70.0f, 1012.0f);
    weatherData->setMeasurements(26.0f, 90.0f, 1011.0f);

    return 0;
}
运行结果

当运行上述代码时,输出将如下:

Current conditions: 25C degrees and 65% humidity
Avg/Max/Min temperature = 25/25/25
Current conditions: 27C degrees and 70% humidity
Avg/Max/Min temperature = 26/27/25
Current conditions: 26C degrees and 90% humidity
Avg/Max/Min temperature = 26/27/25

解释

  1. Observer:这是一个观察者接口,定义了一个update方法,用于更新观察者的状态。
  2. Subject:这是一个主题接口,定义了注册、移除和通知观察者的方法。
  3. WeatherData:这是具体的主题类,维护观察者列表,并实现了注册、移除和通知观察者的方法。当天气数据变化时,通过setMeasurements方法更新数据并通知观察者。
  4. CurrentConditionsDisplayStatisticsDisplay:这两个类是具体的观察者,实现了update方法,分别显示当前天气情况和天气统计信息。
  5. main:创建WeatherDataCurrentConditionsDisplayStatisticsDisplay对象,注册观察者,并模拟天气数据的变化。

总结

观察者模式通过定义一对多的依赖关系,使得多个观察者可以监听一个主题对象,当主题对象状态发生变化时,自动通知所有观察者进行更新。它将观察者与被观察者解耦,提供了一个灵活的机制来实现对象间的通信,适用于需要实时更新的场景。

优化1

update方法的参数数量不能够变化,假如只需要一个风速参数。可以使用一个通用的数据结构来传递参数,比如使用一个结构体或者一个容器(如std::vectorstd::map)来传递参数。这种方法可以灵活地处理不同数量和类型的参数,而不需要修改方法签名。

示例:使用结构体传递参数

我们可以定义一个结构体来包含天气数据,然后在update方法中传递这个结构体。

定义数据结构和接口

首先,定义一个包含天气数据的结构体和观察者接口:

// 天气数据结构体
struct WeatherData {
    float temperature;
    float humidity;
    float pressure;
};

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const WeatherData& data) = 0;
};

通过使用结构体传递参数,可以在不修改方法签名的情况下灵活地传递不同数量和类型的参数。这种方法使观察者模式更加灵活,适用于参数数量和类型可能变化的场景。结构体的使用不仅简化了代码,还增强了代码的可读性和可维护性。

优化2

可以观察到Observer的update仍然需要参数限制,是否参数可以为空?

可以使用“拉取(Pull)”模型来实现观察者模式,其中观察者主动从主题中拉取数据,而不是被动地接收数据。这种方式可以使观察者模式更加灵活,观察者可以根据需要拉取所需的数据,而不是依赖主题推送的数据。

示例:使用拉取模型实现观察者模式

接口和具体类

首先,定义观察者和主题的接口:

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

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

// 主题接口
class Subject {
public:
    virtual ~Subject() = default;
    virtual void registerObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void removeObserver(std::shared_ptr<Observer> observer) = 0;
    virtual void notifyObservers() = 0;
    virtual float getTemperature() const = 0;
    virtual float getHumidity() const = 0;
    virtual float getPressure() const = 0;
};
具体主题类

然后,创建具体的主题类:

class WeatherStation : public Subject {
public:
    void registerObserver(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
    }

    void removeObserver(std::shared_ptr<Observer> observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers() override {
        for (const auto& observer : observers) {
            observer->update();
        }
    }

    void setMeasurements(float temp, float hum, float pres) {
        temperature = temp;
        humidity = hum;
        pressure = pres;
        notifyObservers();
    }

    float getTemperature() const override {
        return temperature;
    }

    float getHumidity() const override {
        return humidity;
    }

    float getPressure() const override {
        return pressure;
    }

private:
    std::vector<std::shared_ptr<Observer>> observers;
    float temperature;
    float humidity;
    float pressure;
};
具体观察者类

接着,创建具体的观察者类:

class CurrentConditionsDisplay : public Observer {
public:
    CurrentConditionsDisplay(std::shared_ptr<Subject> weatherStation)
        : weatherStation(weatherStation) {}

    void update() override {
        float temperature = weatherStation->getTemperature();
        float humidity = weatherStation->getHumidity();
        display(temperature, humidity);
    }

    void display(float temperature, float humidity) {
        std::cout << "Current conditions: " << temperature
                  << "C degrees and " << humidity << "% humidity" << std::endl;
    }

private:
    std::shared_ptr<Subject> weatherStation;
};

class StatisticsDisplay : public Observer {
public:
    StatisticsDisplay(std::shared_ptr<Subject> weatherStation)
        : weatherStation(weatherStation) {}

    void update() override {
        float temperature = weatherStation->getTemperature();
        tempSum += temperature;
        numReadings++;
        if (temperature > maxTemp) {
            maxTemp = temperature;
        }
        if (temperature < minTemp) {
            minTemp = temperature;
        }
        display();
    }

    void display() {
        std::cout << "Avg/Max/Min temperature = " << (tempSum / numReadings)
                  << "/" << maxTemp << "/" << minTemp << std::endl;
    }

private:
    std::shared_ptr<Subject> weatherStation;
    float maxTemp = 0.0f;
    float minTemp = 200.0f;
    float tempSum = 0.0f;
    int numReadings = 0;
};
主函数测试

最后,在主函数中创建主题和观察者对象,并模拟天气数据的变化:

int main() {
    std::shared_ptr<WeatherStation> weatherStation = std::make_shared<WeatherStation>();

    std::shared_ptr<CurrentConditionsDisplay> currentDisplay = std::make_shared<CurrentConditionsDisplay>(weatherStation);
    std::shared_ptr<StatisticsDisplay> statisticsDisplay = std::make_shared<StatisticsDisplay>(weatherStation);

    weatherStation->registerObserver(currentDisplay);
    weatherStation->registerObserver(statisticsDisplay);

    weatherStation->setMeasurements(25.0f, 65.0f, 1013.0f);
    weatherStation->setMeasurements(27.0f, 70.0f, 1012.0f);
    weatherStation->setMeasurements(26.0f, 90.0f, 1011.0f);

    return 0;
}
运行结果

当运行上述代码时,输出将如下:

Current conditions: 25C degrees and 65% humidity
Avg/Max/Min temperature = 25/25/25
Current conditions: 27C degrees and 70% humidity
Avg/Max/Min temperature = 26/27/25
Current conditions: 26C degrees and 90% humidity
Avg/Max/Min temperature = 26/27/25

解释

  1. Observer:这是一个观察者接口,定义了一个update方法,用于通知观察者进行更新。
  2. Subject:这是一个主题接口,定义了注册、移除和通知观察者的方法,还包括获取数据的方法(getTemperaturegetHumiditygetPressure)。
  3. WeatherStation:具体的主题类,实现了注册、移除和通知观察者的方法。它还提供了获取天气数据的方法。
  4. CurrentConditionsDisplayStatisticsDisplay:具体的观察者类,实现了update方法,在需要时从主题中拉取数据进行显示。
  5. main:创建WeatherStationCurrentConditionsDisplayStatisticsDisplay对象,注册观察者,并模拟天气数据的变化。

通过使用“拉取(Pull)”模型,观察者可以在被通知时主动从主题中获取所需的数据。这种方式使得观察者模式更加灵活,观察者可以根据自身需求拉取数据,而不是依赖主题推送的数据。这样设计不仅增加了系统的灵活性,还可以减少数据冗余,提升系统性能。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值