前言
在软件系统中经常会有这样的需求:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化,其中观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。
实际例子:
- 气象站可以将每天预测到的温度、湿度、气压等以公告的形式发布给各种第三方网站,如果天气数据有更新,要能够实时的通知给第三方,这里的气象局就是『被观察者』,第三方网站就是『观察者』
- MVC 模式中的模型与视图的关系也属于观察与被观察
核心思想
观察者模式(Observer Pattern): 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
观察者模式是一种对象行为型模式。
观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
细究的话,发布订阅和观察者有些不同,可以理解成发布订阅模式属于广义上的观察者模式。
想象你订阅了一个YouTube频道:
-
你是观察者(Observer)
-
频道是被观察者(Subject)
-
当你订阅(subscribe)时,新视频发布你会收到通知
-
当你取消订阅(unsubscribe)时,就不再接收通知
基本结构
-
Subject(主题):维护观察者列表,提供添加/删除观察者的接口
-
Observer(观察者):定义更新接口,用于接收主题通知
-
ConcreteSubject(具体主题):状态变化时通知观察者
-
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;
}
输出结果
例子二:
完整多主题多观察者的观察者模式实现
当系统中有多个具体主题和不同类型的观察者时,我们需要更灵活的设计。下面我将展示如何实现这种情况,让不同的观察者可以订阅不同的主题。
改进设计思路
-
每个具体主题维护自己的观察者列表(而不是在基类Subject中)
-
观察者可以区分不同主题(通过主题类型或主题ID)
-
观察者可以有选择地订阅主题
#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;
}
输出结果:
多观察者多主题分析
-
每个具体主题有自己的观察者列表:这样不同主题之间不会相互干扰
-
观察者接口包含多个更新方法:针对不同类型的主题有不同的处理方法
-
主题带有唯一标识:方便观察者区分不同主题
-
观察者可以有选择地实现方法:如温度报警器不处理湿度更新