Menu
CoddyTech

观察者模式

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 94 课,共 104 课。

观察者模式定义了对象之间的一对多依赖关系:当一个对象(主题)改变状态时,它的所有依赖对象(观察者)都会自动收到通知。这非常适合事件系统、UI 更新,或任何需要多个对象响应变化的场景。

这种模式涉及两个关键角色:维护观察者列表并通知它们的主题,以及实现更新接口的观察者:

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

class Observer {
public:
    virtual void update(int value) = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<Observer*> observers;
    int state = 0;
    
public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }
    
    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }
    
    void setState(int value) {
        state = value;
        notify();
    }
    
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }
};

class Display : public Observer {
    std::string name;
public:
    Display(const std::string& n) : name(n) {}
    void update(int value) override {
        std::cout << name << " received: " << value << "\n";
    }
};

int main() {
    Subject sensor;
    Display screen1("Screen1"), screen2("Screen2");
    
    sensor.attach(&screen1);
    sensor.attach(&screen2);
    sensor.setState(42);  // 两个显示器都收到了通知
}

调用 setState() 时,主体会遍历所有已注册的观察者,并调用它们的 update() 方法。观察者可以随时注册或注销,这使系统更加灵活,耦合度也更低。

当一个对象发生变化时需要更新其他对象,并且你不希望这些对象紧密耦合在一起,可以使用 Observer。

challenge icon

挑战

简单

让我们使用观察者模式构建一个 Weather Station 监控系统。你将创建一个跟踪温度数据的气象站,并在温度变化时自动通知多个显示设备:这是该模式在现实世界中的经典应用。

你将把代码分到三个文件中:

  • Observer.h:定义观察者接口和一个具体的显示观察者。

    创建一个抽象 Observer 类,其中包含一个纯虚方法 update(double temperature),观察者通过实现该方法来接收通知;此外还要包含一个虚析构函数。

    然后创建一个继承自 Observer 的 TemperatureDisplay 类。每个显示设备都有一个通过构造函数设置的名称(字符串)。调用 update() 时,它应打印:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h:创建一个维护温度并通知观察者的主题。

    你的 WeatherStation 类应当:

    • 存储观察者指针列表和当前温度(初始化为 0.0)
    • 包含一个用于注册观察者的 attach(Observer* obs) 方法
    • 包含一个用于移除观察者的 detach(Observer* obs) 方法
    • 包含一个 setTemperature(double temp) 方法,用于更新温度并通知所有已附加的观察者
    • 包含一个私有 notify() 方法,用当前温度调用每个观察者的 update()
  • main.cpp:演示观察者模式的实际应用。

    读取三个输入:

    1. 第一个显示设备的名称(字符串)
    2. 第二个显示设备的名称(字符串)
    3. 一个温度值(double)

    创建一个 WeatherStation 和两个 TemperatureDisplay 对象,并使用提供的名称。将两个显示设备都附加到气象站,然后将温度设为输入值。两个显示设备都应自动收到更新并打印消息。

    之后,移除第一个显示设备,并将温度设为比输入值高 5 度的新温度。只有第二个显示设备应收到这次更新。

例如,输入 Kitchen、Bedroom 和 22.5 时:

Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees

输入 Office、Lobby 和 18.0 时:

Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees

请注意,观察者模式创建了一个低耦合系统:气象站不需要了解具体的显示设备,只需通知所有正在监听的对象。移除某个观察者后,它就会自动停止接收更新。

亲手试试

#include <iostream>
#include <string>
#include "WeatherStation.h"

using namespace std;

int main() {
    // 读取输入
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: 创建一个 WeatherStation 对象
    
    // TODO: 使用输入的名称创建两个 TemperatureDisplay 对象
    
    // TODO: 将两个显示器都连接到气象站
    
    // TODO: 将温度设置为输入值
    // (两个显示器都应输出各自的消息)
    
    // TODO: 断开第一个显示器的连接
    
    // TODO: 将温度设置为比输入值高 5 度
    // (只有第二个显示器应输出消息)
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器