观察者模式(Observer Pattern)是一种行为型设计模式,其核心思想是建立对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)会自动收到通知并更新。

这种模式类似现实中的 “订阅 - 发布” 机制(如公众号订阅:公众号更新后,所有订阅者会收到推送)。

观察者模式的核心角色

  1. 被观察者(Subject):
    维护一个观察者列表,提供添加、移除观察者的方法,以及通知所有观察者的方法。当自身状态变化时,主动通知所有观察者。

  2. 观察者(Observer):
    定义一个更新接口,当收到被观察者的通知时,执行相应的更新操作。

  3. 具体被观察者(ConcreteSubject):
    被观察者的具体实现,包含实际的状态数据,状态变化时触发通知。

  4. 具体观察者(ConcreteObserver):
    观察者的具体实现,实现更新接口,根据被观察者的状态变化执行具体逻辑。

适用场景

  • 一个对象的变化需要联动影响多个其他对象(如 GUI 界面中,数据变化时多个组件需要同步更新)。
  • 需要解耦被观察者和观察者(观察者无需知道被观察者的细节,只需响应通知)。
  • 事件监听系统(如按钮点击、消息通知等)。

C++ 代码示例

以 “气象站” 为例:气象站(被观察者)收集天气数据,多个显示面板(观察者)实时显示数据。

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

// 前向声明
class Subject;

// 1. 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    // 纯虚函数:接收被观察者的通知并更新
    virtual void update(float temperature, float humidity, float pressure) = 0;
};

// 2. 被观察者接口
class Subject {
public:
    virtual ~Subject() = default;
    // 添加观察者
    virtual void registerObserver(Observer* observer) = 0;
    // 移除观察者
    virtual void removeObserver(Observer* observer) = 0;
    // 通知所有观察者
    virtual void notifyObservers() = 0;
};

// 3. 具体被观察者:气象站
class WeatherData : public Subject {
private:
    std::vector<Observer*> observers;  // 观察者列表
    float temperature;  // 温度
    float humidity;     // 湿度
    float pressure;     // 气压

public:
    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, pressure);
        }
    }

    // 当气象数据更新时调用,触发通知
    void measurementsChanged() {
        notifyObservers();
    }

    // 设置气象数据(模拟传感器更新)
    void setMeasurements(float temp, float humi, float press) {
        temperature = temp;
        humidity = humi;
        pressure = press;
        measurementsChanged();  // 数据变化,通知观察者
    }
};

// 4. 具体观察者1:当前天气面板
class CurrentConditionsDisplay : public Observer {
private:
    float temperature;
    float humidity;
    // 可选:保存对被观察者的引用,便于后续移除
    Subject* weatherData;

public:
    // 构造时注册为观察者
    CurrentConditionsDisplay(Subject* subject) : weatherData(subject) {
        weatherData->registerObserver(this);
    }

    // 实现更新接口
    void update(float temp, float humi, float press) override {
        temperature = temp;
        humidity = humi;
        display();  // 收到数据后立即显示
    }

    // 显示当前天气
    void display() {
        std::cout << "当前天气:温度 " << temperature 
                  << "℃,湿度 " << humidity << "%" << std::endl;
    }
};

// 5. 具体观察者2:气压面板
class PressureDisplay : public Observer {
private:
    float pressure;

public:
    void update(float temp, float humi, float press) override {
        pressure = press;
        display();
    }

    void display() {
        std::cout << "当前气压:" << pressure << " hPa" << std::endl;
    }
};

// 客户端代码
int main() {
    // 创建被观察者:气象站
    WeatherData* weatherData = new WeatherData();

    // 创建观察者并自动注册
    CurrentConditionsDisplay* currentDisplay = new CurrentConditionsDisplay(weatherData);
    PressureDisplay* pressureDisplay = new PressureDisplay();
    weatherData->registerObserver(pressureDisplay);  // 手动注册

    // 模拟气象数据更新
    std::cout << "--- 第一次数据更新 ---" << std::endl;
    weatherData->setMeasurements(25.5f, 60.0f, 1013.2f);

    std::cout << "\n--- 第二次数据更新 ---" << std::endl;
    weatherData->setMeasurements(26.8f, 55.0f, 1012.5f);

    // 移除一个观察者
    weatherData->removeObserver(pressureDisplay);
    std::cout << "\n--- 移除气压面板后的数据更新 ---" << std::endl;
    weatherData->setMeasurements(24.3f, 65.0f, 1014.0f);

    // 清理资源
    delete currentDisplay;
    delete pressureDisplay;
    delete weatherData;

    return 0;
}

代码说明

  1. 观察者接口(Observer):定义 update() 方法,所有观察者必须实现该方法以响应通知。

  2. 被观察者接口(Subject):定义 registerObserver()、removeObserver()、notifyObservers() 三个核心方法,规范被观察者的行为。

  3. 具体被观察者(WeatherData):

    • 维护气象数据(温度、湿度、气压)和观察者列表。
    • 当数据更新(setMeasurements())时,调用 notifyObservers() 通知所有观察者。
  4. 具体观察者(CurrentConditionsDisplay、PressureDisplay):

    • 实现 update() 方法,接收新数据并更新自身状态。
    • 通过 display() 方法展示数据(具体业务逻辑)。

观察者模式的优缺点

优点:

  • 实现被观察者与观察者的解耦,双方无需知道对方细节,只需依赖接口。
  • 支持动态添加 / 移除观察者,灵活性高。
  • 符合 “开闭原则”,新增观察者无需修改被观察者代码。

缺点:

  • 如果观察者数量过多,通知过程可能耗时较长。
  • 观察者之间可能存在依赖关系,通知顺序可能导致逻辑问题(需谨慎设计)。

观察者模式广泛应用于事件驱动系统(如 GUI 框架、消息队列、订阅系统等),是实现 “松耦合” 设计的重要手段。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐