Observer模式是行为型设计模式的核心之一,核心逻辑是一对多依赖:当被观察者(Subject)的状态变化时,所有注册的观察者(Observer)会被自动通知并执行更新操作。
用生活场景类比:
- 被观察者(Subject):电商平台的商品库存
- 观察者(Observer):库存预警系统、用户订阅提醒、商家后台
- 逻辑:商品库存低于阈值(状态变化),所有关联的观察者都会收到通知并执行对应操作。
核心角色
C++中通过抽象类(纯虚函数) 定义接口,具体类实现接口,核心角色分为4个:
- Observer(抽象观察者) :纯虚类,定义
update更新接口。 - ConcreteObserver(具体观察者) :实现
update,处理具体的更新逻辑。 - Subject(抽象被观察者) :纯虚类,定义
attach(添加观察者)、detach(移除观察者)、notify(通知观察者)接口。 - ConcreteSubject(具体被观察者) :实现Subject接口,维护观察者列表和自身状态,状态变化时触发
notify。
C++代码实现
以「商品库存监控」为例,代码包含完整的类设计、内存管理(C++核心)和测试逻辑:
cpp
#include <iostream>
#include <vector>
#include <algorithm> // 用于find/erase
// ---------------------- 抽象层 ----------------------
// 前向声明:解决循环依赖问题(Observer需要知道Subject,反之亦然)
class Subject;
// 抽象观察者
class Observer {
public:
// 纯虚更新接口:接收被观察者的状态(库存)
virtual void update(int stock) = 0;
// 虚析构:确保子类析构被正确调用(C++内存管理关键)
virtual ~Observer() = default;
};
// 抽象被观察者
class Subject {
public:
// 添加观察者
virtual void attach(Observer* observer) = 0;
// 移除观察者
virtual void detach(Observer* observer) = 0;
// 通知所有观察者
virtual void notify() = 0;
// 虚析构
virtual ~Subject() = default;
};
// ---------------------- 实现层 ----------------------
// 具体被观察者:商品库存管理
class ProductStock : public Subject {
private:
std::vector<Observer*> observers_; // 观察者列表
int stock_; // 核心状态:库存数量
public:
ProductStock(int init_stock) : stock_(init_stock) {}
// 添加观察者:避免重复添加
void attach(Observer* observer) override {
if (std::find(observers_.begin(), observers_.end(), observer) == observers_.end()) {
observers_.push_back(observer);
std::cout << "库存系统:添加观察者 " << typeid(*observer).name() << std::endl;
}
}
// 移除观察者
void detach(Observer* observer) override {
auto it = std::find(observers_.begin(), observers_.end(), observer);
if (it != observers_.end()) {
observers_.erase(it);
std::cout << "库存系统:移除观察者 " << typeid(*observer).name() << std::endl;
}
}
// 通知所有观察者:遍历列表调用update
void notify() override {
std::cout << "\n库存系统:库存变化,通知所有观察者!" << std::endl;
for (auto observer : observers_) {
observer->update(stock_);
}
}
// 更新库存:状态变化时触发通知
void setStock(int new_stock) {
if (new_stock != stock_) { // 仅状态变化时通知
stock_ = new_stock;
notify();
}
}
// 获取当前库存(拉模式可选)
int getStock() const { return stock_; }
};
// 具体观察者1:库存预警系统
class StockAlarm : public Observer {
private:
int alarm_threshold_; // 预警阈值
public:
StockAlarm(int threshold) : alarm_threshold_(threshold) {}
void update(int stock) override {
std::cout << "库存预警系统:当前库存 " << stock << " 件";
if (stock < alarm_threshold_) {
std::cout << " → 低于预警阈值" << alarm_threshold_ << ",触发预警!";
}
std::cout << std::endl;
}
};
// 具体观察者2:商家后台
class MerchantBackend : public Observer {
public:
void update(int stock) override {
std::cout << "商家后台:库存更新为 " << stock << " 件,已同步至系统" << std::endl;
}
};
// ---------------------- 测试主函数 ----------------------
int main() {
// 1. 创建被观察者:初始库存100件的商品
ProductStock product(100);
// 2. 创建观察者:预警阈值50件的预警系统、商家后台
StockAlarm alarm(50);
MerchantBackend backend;
// 3. 注册观察者
product.attach(&alarm);
product.attach(&backend);
// 4. 更新库存(触发通知):库存降至60件
std::cout << "\n=== 第一次库存更新:60件 ===" << std::endl;
product.setStock(60);
// 5. 再次更新库存(触发通知):库存降至40件(低于预警阈值)
std::cout << "\n=== 第二次库存更新:40件 ===" << std::endl;
product.setStock(40);
// 6. 移除商家后台观察者,再次更新库存
std::cout << "\n=== 移除商家后台后,库存更新为30件 ===" << std::endl;
product.detach(&backend);
product.setStock(30);
return 0;
}
编译与运行
bash
# 编译(g++)
g++ observer.cpp -o observer -std=c++11
# 运行
./observer
输出结果
库存系统:添加观察者 9StockAlarm
库存系统:添加观察者 15MerchantBackend
=== 第一次库存更新:60件 ===
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 60 件
商家后台:库存更新为 60 件,已同步至系统
=== 第二次库存更新:40件 ===
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 40 件 → 低于预警阈值50,触发预警!
商家后台:库存更新为 40 件,已同步至系统
=== 移除商家后台后,库存更新为30件 ===
库存系统:移除观察者 15MerchantBackend
库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 30 件 → 低于预警阈值50,触发预警!
C++实现细节
- 前向声明 :
class Subject;解决Observer和Subject的循环依赖问题(观察者需要知道被观察者类型,反之亦然)。 - 虚析构函数 :
virtual ~Observer() = default;是C++的核心规范------确保通过基类指针/引用析构子类对象时,子类析构函数被正确调用,避免内存泄漏。 - 观察者列表管理 :使用
std::vector存储观察者指针,通过std::find避免重复添加,erase安全移除。 - 状态判断优化 :
setStock中判断new_stock != stock_,仅状态真的变化时才触发通知,避免无效调用。 - 类型识别 :
typeid(*observer).name()用于打印观察者类型(不同编译器输出格式可能略有差异,如9StockAlarm表示StockAlarm类)。
推模式 和 拉模式
-
示例中是推模式 :被观察者主动把
stock参数推给观察者(update(int stock))。 -
拉模式(更灵活):观察者主动从被观察者获取状态,只需修改
update接口:cpp// 抽象观察者修改 virtual void update(Subject* subject) = 0; // 具体观察者实现 void update(Subject* subject) override { ProductStock* stock_subject = dynamic_cast<ProductStock*>(subject); if (stock_subject) { int stock = stock_subject->getStock(); // 业务逻辑... } } // 被观察者通知时传this void notify() override { for (auto observer : observers_) { observer->update(this); } }
Observer模式的实际应用
- Qt框架 :
QObject::connect实现的信号与槽机制(核心是观察者模式)。 - GUI事件处理:按钮点击、窗口关闭等事件监听(按钮是Subject,回调函数是Observer)。
- 日志系统:多个日志输出端(控制台、文件、网络)监听日志事件,自动输出。
总结
- C++实现观察者模式的核心是抽象类定义接口+具体类实现,必须注意虚析构和内存管理。
- 关键优势:被观察者与观察者松耦合(仅依赖抽象接口),新增观察者无需修改被观察者代码(符合开闭原则)。
- 核心逻辑:被观察者维护观察者列表,状态变化时遍历列表调用
update,实现自动通知。