观察者模式,一篇就够

前言

在软件系统中经常会有这样的需求:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化,其中观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。

实际例子:

  • 气象站可以将每天预测到的温度、湿度、气压等以公告的形式发布给各种第三方网站,如果天气数据有更新,要能够实时的通知给第三方,这里的气象局就是『被观察者』,第三方网站就是『观察者』
  • MVC 模式中的模型与视图的关系也属于观察与被观察

核心思想

观察者模式(Observer Pattern): 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

观察者模式是一种对象行为型模式

观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

细究的话,发布订阅和观察者有些不同,可以理解成发布订阅模式属于广义上的观察者模式。

想象你订阅了一个YouTube频道:

  • 你是观察者(Observer)

  • 频道是被观察者(Subject)

  • 当你订阅(subscribe)时,新视频发布你会收到通知

  • 当你取消订阅(unsubscribe)时,就不再接收通知

基本结构

  1. Subject(主题):维护观察者列表,提供添加/删除观察者的接口

  2. Observer(观察者):定义更新接口,用于接收主题通知

  3. ConcreteSubject(具体主题):状态变化时通知观察者

  4. ConcreteObserver(具体观察者):实现更新接口,保持与主题状态一致

逻辑代码实现步骤:

1、工作流程

观察者注册到主题:在主函数中,使用 attach 方法将观察者注册到主题。
主题的状态发生变化:调用 setState 方法改变主题的状态。
主题调用通知方法:在 setState 方法中,调用 notify 方法通知所有注册的观察者。
观察者接收到通知后,更新自身状态:每个观察者实现 update 方法,接收到通知后更新自身状态并输出。

第一步:定义观察者接口

首先,定义一个观察者接口,所有观察者都需要实现这个接口。

// 观察者接口
class Observer {
public:
    virtual void update(int state) = 0; // 更新接口
};
第二步:定义主题接口

接下来,定义一个主题接口,主题需要维护观察者列表,并提供添加、删除观察者的方法。

// 主题接口
class Subject {
public:
    virtual void attach(Observer* observer) = 0; // 添加观察者
    virtual void detach(Observer* observer) = 0; // 移除观察者
    virtual void notify() = 0; // 通知观察者
};
第三步:实现具体主题

实现一个具体主题类,维护观察者列表和状态,并在状态变化时通知观察者。

#include <iostream>
#include <vector>
#include <algorithm> // 用于 std::remove

// 具体主题
class ConcreteSubject : public Subject {
private:
    std::vector<Observer*> observers; // 观察者列表
    int state; // 主题的状态

public:
    void attach(Observer* observer) override {
        observers.push_back(observer); // 添加观察者
    }

    void detach(Observer* observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end()); // 移除观察者
    }

    void notify() override {
        for (Observer* observer : observers) {
            observer->update(state); // 通知所有观察者
        }
    }

    void setState(int newState) {
        state = newState; // 更新状态
        notify(); // 状态变化时通知观察者
    }

    int getState() const {
        return state; // 获取当前状态
    }
};
第四步:实现具体观察者

实现观察者类,更新自身状态以反映主题的变化。

// 具体观察者
class ConcreteObserver : public Observer {
private:
    std::string name; // 观察者名称
    int observedState; // 观察者的状态

public:
    ConcreteObserver(const std::string& name) : name(name) {}

    void update(int state) override {
        observedState = state; // 更新观察者的状态
        std::cout << "Observer " << name << " updated with state: " << observedState << std::endl;
    }
};
第五步:主函数

在主函数中,创建主题和观察者对象,注册观察者,并演示状态变化。

// 主函数
int main() {
    ConcreteSubject subject; // 创建具体主题

    ConcreteObserver observer1("Observer 1"); // 创建观察者1
    ConcreteObserver observer2("Observer 2"); // 创建观察者2

    subject.attach(&observer1); // 注册观察者1
    subject.attach(&observer2); // 注册观察者2

    subject.setState(1);  // 状态变化,通知所有观察者
    subject.setState(2);  // 状态变化,通知所有观察者

    return 0;
}

例子一:

完整单一观察者与主题代码实现

以简单的代码构造去理解观察者模式

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

// 前向声明
class Subject;

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(Subject* subject) = 0;
};

// 主题接口
class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(Observer* observer) {
        observers_.push_back(observer);
    }
    virtual void detach(Observer* observer) {
        observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
    }
    virtual void notify() {
        for (Observer* observer : observers_) {
            observer->update(this);
        }
    }
private:
    std::vector<Observer*> observers_;
};

// 具体主题 - 例如温度传感器
class TemperatureSensor : public Subject {
public:
    void setTemperature(float temp) {
        temperature_ = temp;
        notify(); // 温度变化时通知所有观察者
    }
    float getTemperature() const {
        return temperature_;
    }
private:
    float temperature_ = 0.0f;
};

// 具体观察者 - 例如温度显示器
class TemperatureDisplay : public Observer {
public:
    void update(Subject* subject) override {
        if (auto sensor = dynamic_cast<TemperatureSensor*>(subject)) {
            std::cout << "Display: Temperature updated to " 
                      << sensor->getTemperature() << "°C\n";
        }
    }
};

// 另一个观察者 - 例如温度报警器
class TemperatureAlarm : public Observer {
public:
    explicit TemperatureAlarm(float threshold) : threshold_(threshold) {}
    
    void update(Subject* subject) override {
        if (auto sensor = dynamic_cast<TemperatureSensor*>(subject)) {
            float temp = sensor->getTemperature();
            if (temp > threshold_) {
                std::cout << "ALARM! Temperature too high: " << temp << "°C\n";
            }
        }
    }
private:
    float threshold_;
};

int main() {
    TemperatureSensor sensor;
    TemperatureDisplay display;
    TemperatureAlarm alarm(30.0f); // 报警阈值为30°C
    
    // 订阅观察者
    sensor.attach(&display);
    sensor.attach(&alarm);
    
    // 改变温度
    sensor.setTemperature(25.0f);
    sensor.setTemperature(32.5f);
    
    // 取消订阅一个观察者
    sensor.detach(&display);
    sensor.setTemperature(35.0f); // 这次只有报警器会响应
    
    return 0;
}

输出结果

例子二:

完整多主题多观察者的观察者模式实现

当系统中有多个具体主题和不同类型的观察者时,我们需要更灵活的设计。下面我将展示如何实现这种情况,让不同的观察者可以订阅不同的主题。

改进设计思路

  1. 每个具体主题维护自己的观察者列表(而不是在基类Subject中)

  2. 观察者可以区分不同主题(通过主题类型或主题ID)

  3. 观察者可以有选择地订阅主题

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

// 前向声明
class TemperatureSensor;
class HumiditySensor;

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void updateTemperature(TemperatureSensor* sensor) = 0;
    virtual void updateHumidity(HumiditySensor* sensor) = 0;
};

// 主题接口
class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify() = 0;
    virtual std::string getID() const = 0;
};

// 具体主题1 - 温度传感器
class TemperatureSensor : public Subject {
public:
    explicit TemperatureSensor(std::string id) : id_(std::move(id)) {}
    
    void setTemperature(float temp) {
        temperature_ = temp;
        notify();
    }
    
    float getTemperature() const { return temperature_; }
    
    std::string getID() const override { return id_; }
    
    void attach(Observer* observer) override {
        observers_.push_back(observer);
    }
    
    void detach(Observer* observer) override {
        observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), 
                       observers_.end());
    }
    
    void notify() override {
        for (Observer* observer : observers_) {
            observer->updateTemperature(this);
        }
    }

private:
    std::string id_;
    float temperature_ = 0.0f;
    std::vector<Observer*> observers_;
};

// 具体主题2 - 湿度传感器
class HumiditySensor : public Subject {
public:
    explicit HumiditySensor(std::string id) : id_(std::move(id)) {}
    
    void setHumidity(float humidity) {
        humidity_ = humidity;
        notify();
    }
    
    float getHumidity() const { return humidity_; }
    
    std::string getID() const override { return id_; }
    
    void attach(Observer* observer) override {
        observers_.push_back(observer);
    }
    
    void detach(Observer* observer) override {
        observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), 
                       observers_.end());
    }
    
    void notify() override {
        for (Observer* observer : observers_) {
            observer->updateHumidity(this);
        }
    }

private:
    std::string id_;
    float humidity_ = 0.0f;
    std::vector<Observer*> observers_;
};

// 具体观察者1 - 控制台显示器
class ConsoleDisplay : public Observer {
public:
    void updateTemperature(TemperatureSensor* sensor) override {
        std::cout << "ConsoleDisplay - Sensor " << sensor->getID() 
                  << ": Temperature = " << sensor->getTemperature() << "°C\n";
    }
    
    void updateHumidity(HumiditySensor* sensor) override {
        std::cout << "ConsoleDisplay - Sensor " << sensor->getID() 
                  << ": Humidity = " << sensor->getHumidity() << "%\n";
    }
};

// 具体观察者2 - 温度报警器(只关心温度)
class TemperatureAlarm : public Observer {
public:
    explicit TemperatureAlarm(float threshold) : threshold_(threshold) {}
    
    void updateTemperature(TemperatureSensor* sensor) override {
        float temp = sensor->getTemperature();
        if (temp > threshold_) {
            std::cout << "ALARM! Sensor " << sensor->getID() 
                      << ": Temperature " << temp << "°C exceeds threshold "
                      << threshold_ << "°C\n";
        }
    }
    
    void updateHumidity(HumiditySensor* /*sensor*/) override {
        // 湿度变化不处理
    }

private:
    float threshold_;
};

// 具体观察者3 - 湿度记录器(只关心湿度)
class HumidityLogger : public Observer {
public:
    void updateTemperature(TemperatureSensor* /*sensor*/) override {
        // 温度变化不处理
    }
    
    void updateHumidity(HumiditySensor* sensor) override {
        std::cout << "HumidityLogger - Sensor " << sensor->getID() 
                  << " logged humidity: " << sensor->getHumidity() << "%\n";
    }
};

int main() {
    // 创建多个主题
    TemperatureSensor tempSensor1("Temp-1");
    TemperatureSensor tempSensor2("Temp-2");
    HumiditySensor humiditySensor1("Humidity-1");
    
    // 创建观察者
    ConsoleDisplay console;
    TemperatureAlarm alarm(30.0f);
    HumidityLogger logger;
    
    // 订阅关系
    tempSensor1.attach(&console);
    tempSensor1.attach(&alarm);
    
    tempSensor2.attach(&alarm);  // 只订阅报警器
    
    humiditySensor1.attach(&console);
    humiditySensor1.attach(&logger);
    
    // 改变状态
    std::cout << "=== First round of updates ===\n";
    tempSensor1.setTemperature(25.0f);
    tempSensor2.setTemperature(32.0f);
    humiditySensor1.setHumidity(45.5f);
    
    // 改变订阅关系
    tempSensor1.detach(&alarm);
    
    std::cout << "\n=== Second round of updates ===\n";
    tempSensor1.setTemperature(35.0f);  // 不再触发报警
    tempSensor2.setTemperature(28.0f);
    humiditySensor1.setHumidity(50.0f);
    
    return 0;
}

输出结果:

多观察者多主题分析

  1. 每个具体主题有自己的观察者列表:这样不同主题之间不会相互干扰

  2. 观察者接口包含多个更新方法:针对不同类型的主题有不同的处理方法

  3. 主题带有唯一标识:方便观察者区分不同主题

  4. 观察者可以有选择地实现方法:如温度报警器不处理湿度更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值