观察者模式(C++)

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

这种模式的核心是解耦被观察者和观察者,让它们可以独立变化而互不影响。

核心角色

  1. Subject(被观察者/主题)

    • 维护一个观察者列表
    • 提供添加、移除观察者的方法
    • 提供通知所有观察者的方法
  2. Observer(观察者)

    • 定义接收通知的接口
    • 当收到被观察者通知时执行相应操作
  3. ConcreteSubject(具体被观察者)

    • 实现被观察者接口
    • 维护自身状态,状态变化时通知观察者
  4. ConcreteObserver(具体观察者)

    • 实现观察者接口
    • 定义收到通知后的具体处理逻辑

C++实现示例

以下是一个简单的气象站示例,气象站(被观察者)会将温度变化通知给多个显示器(观察者):

复制代码
#include <iostream>
#include <vector>
#include <string>

// 前向声明
class Subject;

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(float temperature) = 0; // 接收温度更新
};

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

// 具体被观察者:气象站
class WeatherStation : public Subject {
private:
    std::vector<Observer*> observers;  // 观察者列表
    float temperature;                 // 温度状态

public:
    void registerObserver(Observer* observer) override {
        observers.push_back(observer);
    }

    void removeObserver(Observer* observer) override {
        // 查找并移除观察者
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }

    void notifyObservers() override {
        // 通知所有观察者
        for (auto observer : observers) {
            observer->update(temperature);
        }
    }

    // 温度变化时调用,触发通知
    void setTemperature(float temp) {
        temperature = temp;
        std::cout << "气象站:温度更新为 " << temperature << "℃" << std::endl;
        notifyObservers();  // 通知所有观察者
    }
};

// 具体观察者:手机显示器
class PhoneDisplay : public Observer {
private:
    std::string name;
    float currentTemp;

public:
    PhoneDisplay(const std::string& n) : name(n), currentTemp(0) {}

    void update(float temperature) override {
        currentTemp = temperature;
        display();
    }

    void display() {
        std::cout << name << " 手机显示:当前温度 " << currentTemp << "℃" << std::endl;
    }
};

// 具体观察者:电脑显示器
class ComputerDisplay : public Observer {
private:
    float currentTemp;

public:
    ComputerDisplay() : currentTemp(0) {}

    void update(float temperature) override {
        currentTemp = temperature;
        display();
    }

    void display() {
        std::cout << "电脑显示:当前温度 " << currentTemp << "℃," 
                  << (currentTemp > 30 ? "天气炎热" : "温度适宜") << std::endl;
    }
};

int main() {
    // 创建被观察者(气象站)
    WeatherStation weatherStation;

    // 创建观察者(显示器)
    PhoneDisplay phone1("小明的手机");
    PhoneDisplay phone2("小红的手机");
    ComputerDisplay computer;

    // 注册观察者
    weatherStation.registerObserver(&phone1);
    weatherStation.registerObserver(&phone2);
    weatherStation.registerObserver(&computer);

    // 温度变化,自动通知所有观察者
    std::cout << "----- 第一次温度变化 -----" << std::endl;
    weatherStation.setTemperature(25.5f);

    std::cout << "\n----- 移除小红的手机 -----" << std::endl;
    weatherStation.removeObserver(&phone2);

    std::cout << "\n----- 第二次温度变化 -----" << std::endl;
    weatherStation.setTemperature(32.0f);

    return 0;
}

代码解析

  1. Observer接口 :定义了update方法,所有观察者都必须实现该方法以接收通知。

  2. Subject接口:定义了管理观察者的方法(注册、移除、通知),是被观察者的抽象。

  3. WeatherStation :具体的被观察者,维护温度状态和观察者列表,当温度变化时会调用notifyObservers通知所有注册的观察者。

  4. PhoneDisplay和ComputerDisplay :具体的观察者,实现了update方法,在收到温度更新时会执行各自的显示逻辑。

观察者模式的优缺点

优点

  • 实现了被观察者和观察者的解耦,两者可以独立变化
  • 观察者数量可动态增减,灵活性高
  • 支持广播通信,被观察者的状态变化可同时通知多个观察者

缺点

  • 如果观察者数量过多,通知过程可能耗时较长
  • 观察者之间没有优先级,无法指定通知顺序
  • 可能导致多余的更新通知(即使观察者不需要该状态)

适用场景

  • 当一个对象的状态变化需要触发多个其他对象的更新时(如事件监听)
  • 当需要动态建立对象间的依赖关系时(如订阅/取消订阅功能)
  • 当一个对象必须通知其他对象,但又不知道这些对象是谁时

常见应用:GUI事件处理(按钮点击触发多个响应)、消息通知系统、股票价格更新、RSS订阅等。