1. 观察者模式概述
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在事件处理系统、消息队列、GUI组件交互等场景中应用广泛。
在C++中实现观察者模式,通常需要以下几个核心组件:
- Subject(主题):维护观察者列表,提供注册和注销观察者的接口
- Observer(观察者):定义更新接口,供主题状态改变时调用
- ConcreteSubject(具体主题):实现主题接口,存储具体状态
- ConcreteObserver(具体观察者):实现观察者接口,保持与主题状态的一致性
2. 模式结构与实现原理
2.1 类图设计
典型的观察者模式类图包含以下关键元素:
code复制+----------------+ +----------------+
| Subject | | Observer |
+----------------+ +----------------+
| +attach(Observer)|<>----| +update() |
| +detach(Observer)| +----------------+
| +notify() | ^
+----------------+ |
^ |
| |
+----------------+ +----------------+
| ConcreteSubject | | ConcreteObserver|
+----------------+ +----------------+
| +getState() | | +update() |
| +setState() | +----------------+
+----------------+
2.2 C++实现核心代码
cpp复制#include <iostream>
#include <vector>
#include <algorithm>
// 前向声明
class Observer;
// Subject接口
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
// Observer接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(Subject* subject) = 0;
};
// 具体Subject实现
class ConcreteSubject : public Subject {
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 (auto observer : observers_) {
observer->update(this);
}
}
int getState() const { return state_; }
void setState(int state) {
state_ = state;
notify(); // 状态改变时自动通知观察者
}
private:
std::vector<Observer*> observers_;
int state_ = 0;
};
// 具体Observer实现
class ConcreteObserver : public Observer {
public:
ConcreteObserver(const std::string& name) : name_(name) {}
void update(Subject* subject) override {
auto concreteSubject = dynamic_cast<ConcreteSubject*>(subject);
if (concreteSubject) {
std::cout << "Observer " << name_ << " received update. New state: "
<< concreteSubject->getState() << std::endl;
}
}
private:
std::string name_;
};
3. 完整示例与使用场景
3.1 示例代码实现
cpp复制int main() {
// 创建主题和观察者
ConcreteSubject subject;
ConcreteObserver observer1("Observer1");
ConcreteObserver observer2("Observer2");
// 注册观察者
subject.attach(&observer1);
subject.attach(&observer2);
// 改变主题状态,触发通知
subject.setState(10);
// 注销一个观察者
subject.detach(&observer1);
// 再次改变状态
subject.setState(20);
return 0;
}
3.2 典型应用场景
- GUI事件处理:按钮点击、菜单选择等用户交互事件的通知机制
- 数据监控系统:当监控数据达到阈值时通知相关处理模块
- 发布-订阅系统:消息队列中的生产者-消费者模型实现
- 游戏开发:角色状态变化时通知UI组件更新显示
- 金融交易系统:价格变动时通知相关交易策略模块
4. 实现细节与优化技巧
4.1 线程安全考虑
在多线程环境下使用观察者模式时,需要注意:
cpp复制#include <mutex>
class ThreadSafeSubject : public Subject {
public:
void attach(Observer* observer) override {
std::lock_guard<std::mutex> lock(mutex_);
observers_.push_back(observer);
}
void detach(Observer* observer) override {
std::lock_guard<std::mutex> lock(mutex_);
observers_.erase(
std::remove(observers_.begin(), observers_.end(), observer),
observers_.end()
);
}
void notify() override {
std::lock_guard<std::mutex> lock(mutex_);
for (auto observer : observers_) {
observer->update(this);
}
}
private:
std::vector<Observer*> observers_;
std::mutex mutex_;
};
4.2 性能优化策略
- 批量通知:当状态频繁变化时,可以合并多次通知
- 懒更新:观察者可以标记需要更新,但不立即执行
- 弱引用:使用weak_ptr避免观察者生命周期管理问题
- 事件过滤:只通知对特定事件感兴趣的观察者
5. 常见问题与解决方案
5.1 内存管理问题
注意:原始指针管理容易导致内存泄漏,建议使用智能指针
改进方案:
cpp复制#include <memory>
class SafeSubject : public Subject {
public:
void attach(std::shared_ptr<Observer> observer) {
observers_.push_back(observer);
}
void detach(std::shared_ptr<Observer> observer) {
observers_.erase(
std::remove(observers_.begin(), observers_.end(), observer),
observers_.end()
);
}
void notify() {
for (auto& observer : observers_) {
if (auto obs = observer.lock()) {
obs->update(this);
}
}
}
private:
std::vector<std::weak_ptr<Observer>> observers_;
};
5.2 循环引用问题
当观察者和主题相互持有时,可能导致循环引用。解决方案:
- 使用weak_ptr打破循环
- 明确生命周期管理责任
- 在适当的时候手动断开引用
5.3 更新顺序控制
有时需要控制观察者的通知顺序,可以通过:
- 为观察者添加优先级字段
- 使用有序容器存储观察者
- 实现自定义的比较函数
6. 现代C++实现变体
6.1 使用function和bind
cpp复制#include <functional>
#include <unordered_set>
class FunctionSubject {
public:
using Callback = std::function<void(int)>;
void attach(Callback cb) {
callbacks_.insert(cb);
}
void detach(Callback cb) {
callbacks_.erase(cb);
}
void notify(int state) {
for (const auto& cb : callbacks_) {
cb(state);
}
}
private:
std::unordered_set<Callback> callbacks_;
};
6.2 信号槽机制实现
cpp复制#include <map>
#include <functional>
#include <vector>
class Signal {
public:
template <typename F>
void connect(F&& slot) {
slots_.emplace_back(std::forward<F>(slot));
}
void emit(int value) {
for (auto& slot : slots_) {
slot(value);
}
}
private:
std::vector<std::function<void(int)>> slots_;
};
// 使用示例
int main() {
Signal valueChanged;
valueChanged.connect([](int v) {
std::cout << "Value changed to: " << v << std::endl;
});
valueChanged.emit(42);
return 0;
}
7. 与其他模式的关系
- 与中介者模式:观察者模式通过分散控制来实现通信,而中介者模式通过集中控制
- 与责任链模式:观察者所有接收者都能得到通知,责任链则沿链传递直到被处理
- 与命令模式:可以将通知实现为命令对象,提供更灵活的通知方式
在实际项目中,我经常将观察者模式与这些模式结合使用,根据具体需求选择最合适的组合方式。比如在游戏开发中,可能会用观察者模式处理UI更新,同时使用命令模式来处理用户输入。