在 C++ 中,观察者模式(Observer Pattern) 是一种行为设计模式,用于建立对象之间的一对多依赖关系。当一个对象的状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并更新自己的状态。这种模式非常适合解耦发布者和订阅者之间的关系,常用于事件处理、UI 更新、日志系统等场景。
观察者模式用于什么?
- 事件通知:
- 当某个事件发生时(例如用户点击按钮),需要通知多个对象执行相应操作。
- 状态同步:
- 一个对象的状态改变时,多个依赖对象需要同步更新(例如数据模型变化时更新视图)。
- 解耦系统:
- 让发布者(Subject)和订阅者(Observer)独立运行,降低耦合度,方便扩展。
- 典型场景:
- GUI 框架(如 Qt 的信号与槽机制)。
- 游戏中角色状态变化通知 UI。
- 分布式系统中的消息订阅。
观察者模式的组成
- Subject(主题/被观察者):
- 维护一个观察者列表,提供添加、删除观察者的方法,并在状态变化时通知所有观察者。
- Observer(观察者):
- 定义一个更新接口,Subject 状态变化时会调用该接口。
- ConcreteSubject:
- Subject 的具体实现,存储状态并在变化时触发通知。
- ConcreteObserver:
- Observer 的具体实现,定义收到通知后的具体行为。
C++ 中的实现
下面是一个简单的 C++ 观察者模式实现,包含基本的添加、移除和通知功能。
#include <iostream>
#include <vector>
#include <algorithm>
// 前向声明
class Subject;
// 观察者基类(抽象接口)
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0; // 纯虚函数,接收通知
};
// 主题类(被观察者)
class Subject {
private:
std::vector<Observer*> observers; // 观察者列表
std::string state; // 主题的状态
public:
void attach(Observer* observer) { // 添加观察者
observers.push_back(observer);
}
void detach(Observer* observer) { // 移除观察者
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify() { // 通知所有观察者
for (Observer* observer : observers) {
observer->update(state);
}
}
void setState(const std::string& newState) { // 修改状态并通知
state = newState;
notify();
}
std::string getState() const {
return state;
}
};
// 具体观察者 A
class ConcreteObserverA : public Observer {
private:
std::string name;
public:
ConcreteObserverA(const std::string& n) : name(n) {}
void update(const std::string& message) override {
std::cout << name << " received update: " << message << "\n";
}
};
// 具体观察者 B
class ConcreteObserverB : public Observer {
private:
std::string name;
public:
ConcreteObserverB(const std::string& n) : name(n) {}
void update(const std::string& message) override {
std::cout << name << " processed message: " << message << "\n";
}
};
// 测试代码
int main() {
Subject subject;
ConcreteObserverA observerA("Observer A");
ConcreteObserverB observerB("Observer B");
// 注册观察者
subject.attach(&observerA);
subject.attach(&observerB);
// 修改状态,触发通知
subject.setState("State changed to 1");
std::cout << "----\n";
// 移除一个观察者
subject.detach(&observerA);
// 再次修改状态
subject.setState("State changed to 2");
return 0;
}
输出:
Observer A received update: State changed to 1
Observer B processed message: State changed to 1
----
Observer B processed message: State changed to 2
实现要点解析
观察者接口(Observer):
- 定义纯虚函数
update()
,具体观察者需要实现它。 - 使用虚析构函数确保派生类对象正确销毁。
- 定义纯虚函数
主题(Subject):
- 使用
std::vector<Observer*>
存储观察者指针,动态管理订阅关系。 attach()
和detach()
分别添加和移除观察者。notify()
遍历列表,调用每个观察者的update()
。
- 使用
状态管理:
setState()
修改主题状态并触发notify()
,实现“状态变化 -> 通知”的逻辑。
具体观察者:
ConcreteObserverA
和ConcreteObserverB
是不同类型的观察者,展示了如何自定义响应行为。
改进建议
上述实现是一个基础版本,实际应用中可以根据需求优化:
线程安全:
- 如果在多线程环境下使用,需要在
attach()
、detach()
和notify()
中加锁(例如使用std::mutex
)。
std::mutex mtx; void attach(Observer* observer) { std::lock_guard<std::mutex> lock(mtx); observers.push_back(observer); }
- 如果在多线程环境下使用,需要在
智能指针:
- 用
std::shared_ptr<Observer>
替代裸指针,避免手动管理内存。
std::vector<std::shared_ptr<Observer>> observers; void attach(std::shared_ptr<Observer> observer) { observers.push_back(observer); }
- 用
传递更多信息:
update()
可以携带更多参数(如事件类型、数据指针),而不是简单的一个字符串。
弱引用:
- 使用
std::weak_ptr
避免观察者被意外销毁后仍留在列表中的问题。
- 使用
实际应用示例
假设你实现一个温度传感器系统:
- Subject:温度传感器,记录温度变化。
- Observers:显示屏(显示温度)、警报器(温度过高时报警)。
class TemperatureSensor {
private:
std::vector<Observer*> observers;
double temperature;
public:
void attach(Observer* o) { observers.push_back(o); }
void notify() {
for (auto* o : observers) o->update(std::to_string(temperature));
}
void setTemperature(double temp) {
temperature = temp;
notify();
}
};
class Display : public Observer {
public:
void update(const std::string& message) override {
std::cout << "Display: Temperature is " << message << "°C\n";
}
};
class Alarm : public Observer {
public:
void update(const std::string& message) override {
double temp = std::stod(message);
if (temp > 30.0) {
std::cout << "Alarm: Temperature too high! " << temp << "°C\n";
}
}
};
int main() {
TemperatureSensor sensor;
Display display;
Alarm alarm;
sensor.attach(&display);
sensor.attach(&alarm);
sensor.setTemperature(25.0);
sensor.setTemperature(35.0);
return 0;
}
输出:
Display: Temperature is 25°C
Display: Temperature is 35°C
Alarm: Temperature too high! 35°C
总结
- 用途:观察者模式用于在对象状态变化时通知多个依赖对象,广泛应用于事件驱动的系统。
- 实现:通过 Subject 管理观察者列表,Observer 定义更新接口,结合动态注册和通知实现松耦合。
- 优势:灵活、可扩展,Subject 和 Observer 可以独立开发。
- 注意:多线程环境下需加锁,内存管理可优化。