观察者模式是软件设计模式中行为型模式的经典代表,它定义了对象之间一对多的依赖关系。当被观察对象状态发生改变时,所有依赖于它的观察者对象都会自动收到通知并更新。
观察者模式最早由GoF(Gang of Four)在《设计模式:可复用面向对象软件的基础》一书中提出。在C++中实现观察者模式需要考虑以下几个关键点:
观察者模式最大的价值在于解耦。在传统的紧耦合设计中,对象之间直接相互调用,导致系统难以维护和扩展。通过观察者模式,我们可以实现:
一个完整的观察者模式实现通常包含以下组件:
cpp复制// 观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(int value) = 0;
};
// 被观察者接口
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(std::shared_ptr<Observer> obs) = 0;
virtual void detach(std::shared_ptr<Observer> obs) = 0;
virtual void notify() = 0;
};
在实际编码中,有几个关键细节需要注意:
智能指针的使用:推荐使用std::shared_ptr管理观察者生命周期,避免裸指针带来的内存泄漏风险
线程安全考虑:在多线程环境下,观察者列表的修改和遍历需要加锁保护:
cpp复制class ThreadSafeSubject : public Subject {
public:
void attach(std::shared_ptr<Observer> obs) override {
std::lock_guard<std::mutex> lock(mutex_);
observers_.push_back(obs);
}
// 其他方法实现...
private:
std::mutex mutex_;
std::vector<std::shared_ptr<Observer>> observers_;
};
基于观察者模式可以构建更强大的事件总线系统:
cpp复制class EventBus {
public:
template<typename EventType>
void subscribe(std::function<void(const EventType&)> handler) {
// 实现类型安全的订阅机制
}
template<typename EventType>
void publish(const EventType& event) {
// 实现事件发布
}
};
观察者模式常与其他设计模式配合使用:
在图形界面开发中,观察者模式被广泛应用:
cpp复制// 按钮点击事件处理示例
class Button {
public:
void onClick(std::function<void()> handler) {
clickHandlers_.push_back(handler);
}
void click() {
for(auto& handler : clickHandlers_) {
handler();
}
}
private:
std::vector<std::function<void()>> clickHandlers_;
};
游戏引擎中常用观察者模式处理各种游戏事件:
cpp复制// 游戏事件系统示例
class GameEventSystem {
public:
void registerHandler(EventType type, EventHandler handler) {
eventHandlers_[type].push_back(handler);
}
void triggerEvent(EventType type, EventData data) {
for(auto& handler : eventHandlers_[type]) {
handler(data);
}
}
private:
std::unordered_map<EventType, std::vector<EventHandler>> eventHandlers_;
};
针对上述问题,可以采用以下优化方案:
cpp复制void setState(int newState) {
if(shouldNotify(newState)) {
state_ = newState;
notify();
}
}
cpp复制void asyncNotify() {
executor_.submit([this](){
notify();
});
}
cpp复制std::vector<std::weak_ptr<Observer>> observers_;
现代C++可以使用函数对象简化观察者模式:
cpp复制class Subject {
public:
using ObserverFunc = std::function<void(int)>;
void addObserver(ObserverFunc func) {
observers_.push_back(func);
}
void notify(int value) {
for(auto& obs : observers_) {
obs(value);
}
}
private:
std::vector<ObserverFunc> observers_;
};
通过模板可以实现类型安全的观察者模式:
cpp复制template<typename T>
class Observable {
public:
void subscribe(std::function<void(const T&)> observer) {
observers_.push_back(observer);
}
void notify(const T& value) {
for(auto& obs : observers_) {
obs(value);
}
}
private:
std::vector<std::function<void(const T&)>> observers_;
};
观察者模式的测试要点包括:
示例测试用例:
cpp复制TEST(ObserverPattern, BasicNotification) {
auto subject = std::make_shared<ConcreteSubject>();
auto observer = std::make_shared<MockObserver>();
subject->attach(observer);
subject->setState(42);
EXPECT_EQ(observer->lastValue(), 42);
}
观察者未收到通知:
内存泄漏检测:
建议按以下结构组织观察者模式代码:
code复制observer_pattern/
├── include/
│ ├── observer.h // 观察者接口
│ ├── subject.h // 被观察者接口
│ └── concrete_impl/ // 具体实现
├── src/
│ └── ... // 实现文件
└── test/ // 测试代码
良好的文档应该包含:
虽然相似,但有重要区别:
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 耦合度 | 直接耦合 | 通过消息代理解耦 |
| 灵活性 | 较低 | 更高 |
| 复杂度 | 简单 | 较复杂 |
| 适用场景 | 对象间直接通知 | 系统间消息传递 |
回调函数可以看作是观察者模式的简化形式:
回调优势:
观察者优势:
在多年的C++项目实践中,我总结了以下经验教训:
避免过度使用:不是所有状态变化都需要观察者模式,简单场景用直接调用更高效
注意线程切换:跨线程通知要考虑上下文切换开销
性能监控:实现性能计数器监控通知频率和处理时间
异常处理:观察者的update方法应该捕获异常,避免影响其他观察者
调试支持:实现观察者调试接口,便于问题排查
一个实用的调试观察者实现示例:
cpp复制class DebugObserver : public Observer {
public:
void update(int value) override {
try {
// 实际处理逻辑
lastValue_ = value;
++notificationCount_;
} catch(...) {
logError("Observer处理失败");
}
}
int getNotificationCount() const { return notificationCount_; }
private:
int lastValue_ = 0;
int notificationCount_ = 0;
};
在大型项目中,观察者模式的正确使用可以显著提高代码的可维护性和扩展性。关键在于平衡灵活性和性能,根据具体场景选择合适的实现方式。