文章目录
前言
本文主要探讨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和实时数据更新。 |
| 劣势 |
| 劣势 | 性能影响:大量观察者时,通知所有观察者可能导致性能下降。 |
| 劣势 | 通知泛滥:频繁的状态更新可能使观察者接收过多不必要的通知。 |
| 劣势 | 更新顺序不确定:观察者的通知顺序不固定,可能影响系统行为。 |
| 适用场景 |
| 适用场景 | 用户界面:组件状态变化时,其他相关组件需要自动更新。 |
| 适用场景 | 实时数据监控:如股票价格、天气变化等,需要即时通知用户。 |
| 适用场景 | 事件驱动系统:例如在游戏开发中,处理事件发生和响应。 |