C++设计模式之行为型模式:观察者模式(Observer)
观察者模式(Observer)是行为型设计模式的一种,它定义了对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并更新。这种模式也被称为“发布-订阅”模式,广泛应用于事件处理、消息通知等场景。
一、核心思想与角色
观察者模式的核心是“状态变化通知”,通过解耦被观察者和观察者,实现灵活的消息传递机制。其核心角色如下:
角色名称 | 核心职责 |
---|---|
抽象被观察者(Subject) | 定义被观察者的接口,提供添加、移除观察者和通知所有观察者的方法。 |
具体被观察者(ConcreteSubject) | 实现抽象被观察者接口,存储状态,状态变化时调用通知方法。 |
抽象观察者(Observer) | 定义观察者的接口,声明更新方法(当被观察者状态变化时被调用)。 |
具体观察者(ConcreteObserver) | 实现抽象观察者接口,在更新方法中响应被观察者的状态变化。 |
核心思想:被观察者维护一个观察者列表,状态变化时主动通知所有观察者;观察者被动接收通知并执行相应操作,两者通过抽象接口交互,实现松耦合。
二、实现示例(气象站与显示设备)
假设我们需要设计一个气象站系统:气象站(被观察者)收集温度、湿度数据,多个显示设备(观察者,如手机APP、控制台显示器)实时显示这些数据。使用观察者模式可实现数据变化时自动更新所有显示设备:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>// 1. 抽象观察者
class Observer {
public:// 纯虚方法:更新数据(温度、湿度)virtual void update(float temperature, float humidity) = 0;virtual ~Observer() = default;
};// 2. 抽象被观察者
class Subject {
public:// 添加观察者virtual void registerObserver(Observer* observer) = 0;// 移除观察者virtual void removeObserver(Observer* observer) = 0;// 通知所有观察者virtual void notifyObservers() = 0;virtual ~Subject() = default;
};// 3. 具体被观察者:气象站
class WeatherStation : public Subject {
private:std::vector<Observer*> observers; // 观察者列表float temperature; // 温度float humidity; // 湿度public:WeatherStation() : temperature(0.0f), humidity(0.0f) {}// 添加观察者到列表void registerObserver(Observer* observer) override {observers.push_back(observer);}// 从列表移除观察者void removeObserver(Observer* observer) override {auto it = std::find(observers.begin(), observers.end(), observer);if (it != observers.end()) {observers.erase(it);}}// 通知所有观察者(传递当前状态)void notifyObservers() override {for (Observer* observer : observers) {observer->update(temperature, humidity);}}// 气象站数据更新(外部调用,如传感器采集)void setMeasurements(float temp, float hum) {temperature = temp;humidity = hum;std::cout << "\n气象站数据更新:温度 " << temp << "℃,湿度 " << hum << "%" << std::endl;notifyObservers(); // 数据更新后通知观察者}
};// 3. 具体观察者1:手机APP显示器
class PhoneDisplay : public Observer {
private:std::string name; // 设备名称public:PhoneDisplay(const std::string& n) : name(n) {}// 接收更新并显示void update(float temperature, float humidity) override {std::cout << name << " 显示:当前温度 " << temperature << "℃,湿度 " << humidity << "%" << std::endl;}
};// 3. 具体观察者2:控制台显示器
class ConsoleDisplay : public Observer {
public:// 接收更新并显示(带详细信息)void update(float temperature, float humidity) override {std::cout << "控制台显示:温度 " << temperature << "℃,湿度 " << humidity << "% | "<< (temperature > 30 ? "天气炎热" : "温度适宜") << std::endl;}
};// 客户端代码:组装系统并测试
int main() {// 创建被观察者(气象站)WeatherStation* station = new WeatherStation();// 创建观察者(显示设备)Observer* phone1 = new PhoneDisplay("小明的手机");Observer* phone2 = new PhoneDisplay("小红的手机");Observer* console = new ConsoleDisplay();// 注册观察者station->registerObserver(phone1);station->registerObserver(phone2);station->registerObserver(console);// 气象站更新数据(触发通知)station->setMeasurements(25.5f, 60.0f);// 移除一个观察者(小红的手机)station->removeObserver(phone2);std::cout << "\n--- 移除小红的手机后 ---" << std::endl;// 再次更新数据station->setMeasurements(32.0f, 50.0f);// 释放资源delete console;delete phone2;delete phone1;delete station;return 0;
}
三、代码解析
-
抽象观察者(Observer):
定义update()
纯虚方法,声明观察者接收通知后应执行的操作,参数为被观察者的状态数据(温度、湿度)。 -
抽象被观察者(Subject):
定义registerObserver()
(添加观察者)、removeObserver()
(移除观察者)和notifyObservers()
(通知观察者)接口,规范被观察者的行为。 -
具体被观察者(WeatherStation):
- 维护
observers
列表存储所有注册的观察者。 setMeasurements()
方法更新气象数据,并调用notifyObservers()
通知所有观察者。notifyObservers()
遍历观察者列表,调用每个观察者的update()
方法传递最新状态。
- 维护
-
具体观察者:
PhoneDisplay
:实现update()
方法,简单显示温度和湿度。ConsoleDisplay
:实现update()
方法,除显示数据外,还添加了天气评价(如“天气炎热”)。
两者都只关注如何处理接收到的状态,无需知道被观察者的具体类型。
-
客户端使用:
客户端创建被观察者和观察者,通过registerObserver()
建立关联;当被观察者状态变化时,所有注册的观察者自动收到通知并更新。
四、核心优势与适用场景
优势
- 松耦合:被观察者无需知道观察者的具体类型和数量,只需通过抽象接口交互,一方变化不影响另一方。
- 动态关联:可在运行时动态添加或移除观察者(如示例中移除“小红的手机”),灵活性高。
- 广播通信:被观察者状态变化时,自动通知所有相关观察者,无需逐个调用。
- 单一职责:被观察者专注于状态管理,观察者专注于状态处理,符合单一职责原则。
适用场景
- 事件驱动系统:如GUI事件(按钮点击、窗口关闭)、消息队列通知。
- 数据实时更新:如股票行情显示、气象数据监控、实时监控系统。
- 发布-订阅模型:如微信公众号、邮件订阅、消息推送服务。
- 状态变化需要多方响应:当一个对象的变化需要触发多个其他对象的操作时。
五、与其他模式的区别
模式 | 核心差异点 |
---|---|
观察者模式 | 一对多依赖,被观察者主动通知观察者,强调“状态变化通知”。 |
中介者模式 | 多对多交互通过中介者集中处理,强调“交互集中控制”。 |
发布-订阅模式 | 观察者模式的变种,通过消息队列/主题实现松耦合,通常异步通信。 |
职责链模式 | 请求沿链传递,由一个处理者处理,强调“请求分发”。 |
六、实践建议
- 避免循环依赖:观察者和被观察者之间应避免双向依赖,防止通知时出现无限循环。
- 处理通知顺序:若观察者更新有先后顺序要求,需在被观察者中明确通知顺序,或使用优先级队列。
- 支持异步通知:对于耗时的观察者操作,可使用异步通知(如多线程)避免阻塞被观察者。
- 提供拉取模式:除示例中的“推模式”(被观察者主动传递数据),还可支持“拉模式”(观察者按需从被观察者获取数据),减少不必要的数据传递。
- 清理资源:观察者销毁前需从被观察者中移除自己,避免访问已销毁对象(悬垂指针)。
观察者模式的核心价值在于“实现对象间的解耦通信”,它通过定义清晰的通知机制,使状态变化能自动传递给所有依赖对象,是构建事件驱动和实时响应系统的理想选择。在需要多方响应同一状态变化的场景中,观察者模式能显著提高系统的灵活性和可扩展性。