1. 观察者模式的核心概念与应用场景
观察者模式(Observer Pattern)是软件设计中最常用的行为型模式之一,它定义了对象间一对多的依赖关系。当被观察对象状态发生改变时,所有依赖于它的观察者都会自动收到通知并更新。这种模式在事件处理系统、消息队列、GUI组件交互等场景中应用广泛。
在C++中实现观察者模式时,我们通常会遇到几个关键问题:如何避免观察者持有被观察对象的强引用导致内存泄漏?如何处理多线程环境下的通知顺序问题?如何优化大量观察者时的通知效率?这些都是实际工程中必须面对的挑战。
提示:观察者模式与发布-订阅模式(Pub-Sub)有相似之处,但前者通常采用同步通知机制,后者则多采用异步消息队列实现。
2. 经典观察者模式的C++实现
2.1 基础接口设计
我们先定义观察者模式的三个核心组件:
cpp复制// 观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
// 被观察者接口
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
// 具体被观察者实现
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(state_);
}
}
void setState(const std::string& state) {
state_ = state;
notify();
}
private:
std::vector<Observer*> observers_;
std::string state_;
};
这种实现虽然简单,但存在几个明显问题:
- 使用裸指针管理观察者生命周期,容易导致悬垂指针
- 通知过程是同步阻塞的,可能影响性能
- 没有考虑线程安全问题
2.2 改进版实现:使用智能指针
为了解决内存管理问题,我们可以引入std::shared_ptr和std::weak_ptr:
cpp复制class Observer : public std::enable_shared_from_this<Observer> {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(std::shared_ptr<Observer> observer) = 0;
virtual void detach(std::shared_ptr<Observer> observer) = 0;
virtual void notify() = 0;
};
class ConcreteSubject : public Subject {
public:
void attach(std::shared_ptr<Observer> observer) override {
observers_.push_back(observer);
}
void detach(std::shared_ptr<Observer> observer) override {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
observers_.end());
}
void notify() override {
for (auto& weak_observer : observers_) {
if (auto observer = weak_observer.lock()) {
observer->update(state_);
}
}
}
private:
std::vector<std::weak_ptr<Observer>> observers_;
std::string state_;
};
这种实现通过weak_ptr避免了循环引用问题,同时确保观察者对象存活时才进行通知。
3. 线程安全的观察者模式实现
在多线程环境中,我们需要考虑以下几个关键点:
- 观察者的注册/注销需要线程安全
- 通知过程需要避免死锁
- 观察者的更新方法可能在不同线程执行
3.1 使用互斥锁保护关键区域
cpp复制#include <mutex>
#include <shared_mutex>
class ThreadSafeSubject : public Subject {
public:
void attach(std::shared_ptr<Observer> observer) override {
std::unique_lock lock(mutex_);
observers_.push_back(observer);
}
void detach(std::shared_ptr<Observer> observer) override {
std::unique_lock lock(mutex_);
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
observers_.end());
}
void notify() override {
std::shared_lock lock(mutex_);
auto local_copy = observers_; // 创建本地副本
lock.unlock();
for (auto& observer : local_copy) {
if (auto ptr = observer.lock()) {
ptr->update(state_);
}
}
}
private:
mutable std::shared_mutex mutex_;
std::vector<std::weak_ptr<Observer>> observers_;
std::string state_;
};
注意:这里使用了读写锁(
shared_mutex),因为通知操作通常是读密集型的。创建本地副本可以最小化持有锁的时间。
3.2 异步通知实现
对于需要非阻塞通知的场景,我们可以使用std::async:
cpp复制void ThreadSafeSubject::notify() {
std::shared_lock lock(mutex_);
auto local_copy = observers_;
lock.unlock();
std::vector<std::future<void>> futures;
for (auto& observer : local_copy) {
if (auto ptr = observer.lock()) {
futures.emplace_back(std::async(std::launch::async,
[](std::shared_ptr<Observer> obs, std::string state) {
obs->update(state);
}, ptr, state_));
}
}
// 可选:等待所有通知完成
for (auto& f : futures) {
f.wait();
}
}
4. 性能优化与高级技巧
4.1 观察者分组与批量通知
当观察者数量庞大时,可以按事件类型分组:
cpp复制enum class EventType {
DATA_UPDATE,
STATUS_CHANGE,
ERROR_OCCURRED
};
class AdvancedSubject {
public:
void attach(EventType type, std::shared_ptr<Observer> observer) {
std::unique_lock lock(mutex_);
observers_[type].push_back(observer);
}
void notify(EventType type) {
std::shared_lock lock(mutex_);
if (auto it = observers_.find(type); it != observers_.end()) {
auto local_copy = it->second;
lock.unlock();
for (auto& observer : local_copy) {
if (auto ptr = observer.lock()) {
ptr->update(getStateForEvent(type));
}
}
}
}
private:
mutable std::shared_mutex mutex_;
std::unordered_map<EventType, std::vector<std::weak_ptr<Observer>>> observers_;
};
4.2 使用事件队列解耦
对于高频事件,可以引入事件队列:
cpp复制#include <queue>
#include <condition_variable>
class EventQueueSubject {
public:
void attach(std::shared_ptr<Observer> observer) {
std::unique_lock lock(mutex_);
observers_.push_back(observer);
}
void postEvent(const std::string& event) {
{
std::unique_lock lock(event_mutex_);
event_queue_.push(event);
}
event_cv_.notify_one();
}
void startDispatchThread() {
dispatch_thread_ = std::thread([this]() {
while (running_) {
std::string event;
{
std::unique_lock lock(event_mutex_);
event_cv_.wait(lock, [this]() {
return !event_queue_.empty() || !running_;
});
if (!running_) break;
event = event_queue_.front();
event_queue_.pop();
}
std::shared_lock lock(mutex_);
auto local_copy = observers_;
lock.unlock();
for (auto& observer : local_copy) {
if (auto ptr = observer.lock()) {
ptr->update(event);
}
}
}
});
}
void stopDispatchThread() {
running_ = false;
event_cv_.notify_all();
if (dispatch_thread_.joinable()) {
dispatch_thread_.join();
}
}
private:
std::thread dispatch_thread_;
std::atomic<bool> running_{true};
std::mutex event_mutex_;
std::condition_variable event_cv_;
std::queue<std::string> event_queue_;
mutable std::shared_mutex mutex_;
std::vector<std::weak_ptr<Observer>> observers_;
};
5. 实际应用案例:GUI事件系统
让我们实现一个简单的GUI按钮点击事件系统:
cpp复制class ButtonClickObserver : public Observer {
public:
explicit ButtonClickObserver(const std::string& name) : name_(name) {}
void update(const std::string& message) override {
std::cout << name_ << " received button click: " << message << "\n";
}
private:
std::string name_;
};
class Button : public Subject {
public:
void click() {
std::cout << "Button clicked!\n";
setState("clicked at " + getCurrentTime());
}
private:
static std::string getCurrentTime() {
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
return ss.str();
}
};
int main() {
auto button = std::make_shared<Button>();
auto observer1 = std::make_shared<ButtonClickObserver>("Logger");
auto observer2 = std::make_shared<ButtonClickObserver>("Analytics");
button->attach(observer1);
button->attach(observer2);
button->click();
return 0;
}
在这个实现中,我们创建了一个按钮和两个观察者(日志记录器和分析工具)。当按钮被点击时,两个观察者都会收到通知。
6. 常见问题与解决方案
6.1 观察者处理时间过长
当观察者的update方法执行时间过长时,会阻塞通知线程。解决方案:
- 使用异步通知(如前面所示的
std::async方法) - 在观察者内部使用工作队列
- 设置超时机制
6.2 观察者抛出异常
如果观察者的update方法可能抛出异常,需要确保不影响其他观察者:
cpp复制void SafeNotify() {
auto local_copy = getObserversCopy();
for (auto& observer : local_copy) {
try {
if (auto ptr = observer.lock()) {
ptr->update(state_);
}
} catch (const std::exception& e) {
std::cerr << "Observer failed: " << e.what() << "\n";
}
}
}
6.3 循环通知问题
当观察者反过来修改被观察对象状态时,可能导致无限递归。解决方案:
- 检测递归深度
- 使用事件ID去重
- 分离状态修改和通知操作
7. 现代C++的改进实现
C++17和C++20引入的新特性可以让我们写出更简洁安全的观察者模式:
7.1 使用std::function作为观察者
cpp复制class FunctionalSubject {
public:
using ObserverHandle = size_t;
ObserverHandle addObserver(std::function<void(const std::string&)> callback) {
std::unique_lock lock(mutex_);
ObserverHandle handle = next_handle_++;
observers_[handle] = callback;
return handle;
}
void removeObserver(ObserverHandle handle) {
std::unique_lock lock(mutex_);
observers_.erase(handle);
}
void notify(const std::string& message) {
std::shared_lock lock(mutex_);
auto local_copy = observers_;
lock.unlock();
for (auto& [handle, callback] : local_copy) {
callback(message);
}
}
private:
mutable std::shared_mutex mutex_;
std::unordered_map<ObserverHandle, std::function<void(const std::string&)>> observers_;
ObserverHandle next_handle_ = 0;
};
7.2 使用协程实现异步观察者
C++20协程可以简化异步通知代码:
cpp复制#include <coroutine>
struct NotificationAwaiter {
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h) {
// 将协程句柄存储起来,在事件到来时恢复
stored_handle_ = h;
}
std::string await_resume() const { return received_message_; }
void notify(std::string message) {
received_message_ = std::move(message);
if (stored_handle_) {
stored_handle_.resume();
}
}
private:
std::coroutine_handle<> stored_handle_;
std::string received_message_;
};
class CoroutineObserver {
public:
NotificationAwaiter waitForNotification() {
return NotificationAwaiter{};
}
};
8. 测试与调试技巧
8.1 单元测试观察者模式
使用Google Test框架测试观察者模式:
cpp复制#include <gtest/gtest.h>
TEST(ObserverPatternTest, BasicNotification) {
auto subject = std::make_shared<ConcreteSubject>();
bool notified = false;
auto observer = std::make_shared<Observer>([&](const std::string&) {
notified = true;
});
subject->attach(observer);
subject->setState("test");
EXPECT_TRUE(notified);
}
TEST(ObserverPatternTest, ThreadSafety) {
auto subject = std::make_shared<ThreadSafeSubject>();
std::atomic<int> notification_count{0};
constexpr int kThreadCount = 10;
constexpr int kObserversPerThread = 100;
std::vector<std::thread> threads;
for (int i = 0; i < kThreadCount; ++i) {
threads.emplace_back([&]() {
std::vector<std::shared_ptr<Observer>> observers;
for (int j = 0; j < kObserversPerThread; ++j) {
auto observer = std::make_shared<Observer>([&](const std::string&) {
++notification_count;
});
subject->attach(observer);
observers.push_back(observer);
}
subject->notify();
for (auto& observer : observers) {
subject->detach(observer);
}
});
}
for (auto& t : threads) {
t.join();
}
EXPECT_EQ(notification_count, kThreadCount * kObserversPerThread);
}
8.2 性能分析与优化
使用benchmark库测试不同实现的性能:
cpp复制#include <benchmark/benchmark.h>
static void BM_SimpleNotify(benchmark::State& state) {
SimpleSubject subject;
std::vector<std::shared_ptr<Observer>> observers;
for (int i = 0; i < state.range(0); ++i) {
observers.push_back(std::make_shared<Observer>([](const std::string&){}));
subject.attach(observers.back());
}
for (auto _ : state) {
subject.notify();
}
}
BENCHMARK(BM_SimpleNotify)->Range(8, 8<<10);
static void BM_ThreadSafeNotify(benchmark::State& state) {
ThreadSafeSubject subject;
std::vector<std::shared_ptr<Observer>> observers;
for (int i = 0; i < state.range(0); ++i) {
observers.push_back(std::make_shared<Observer>([](const std::string&){}));
subject.attach(observers.back());
}
for (auto _ : state) {
subject.notify();
}
}
BENCHMARK(BM_ThreadSafeNotify)->Range(8, 8<<10);
9. 与其他设计模式的结合
9.1 观察者模式与中介者模式
中介者模式可以协调多个观察者之间的交互:
cpp复制class Mediator {
public:
void registerComponent(const std::string& id, std::shared_ptr<Observer> observer) {
components_[id] = observer;
}
void notify(const std::string& from, const std::string& message) {
if (auto it = routing_table_.find(from); it != routing_table_.end()) {
for (const auto& to : it->second) {
if (auto component = components_.find(to); component != components_.end()) {
if (auto ptr = component->second.lock()) {
ptr->update(message);
}
}
}
}
}
void addRoute(const std::string& from, const std::string& to) {
routing_table_[from].insert(to);
}
private:
std::unordered_map<std::string, std::weak_ptr<Observer>> components_;
std::unordered_map<std::string, std::set<std::string>> routing_table_;
};
9.2 观察者模式与状态模式
状态模式可以通过观察者通知状态变化:
cpp复制class StateMachine : public Subject {
public:
void changeState(std::unique_ptr<State> new_state) {
current_state_ = std::move(new_state);
notify();
}
void doWork() {
current_state_->handle(*this);
}
private:
std::unique_ptr<State> current_state_;
};
10. 实际项目中的最佳实践
- 生命周期管理:始终使用智能指针管理观察者生命周期,避免内存泄漏
- 接口设计:保持观察者接口尽可能简单,避免过度设计
- 性能考量:对于高频事件,考虑批量通知或异步处理
- 错误处理:确保单个观察者的失败不会影响整个系统
- 文档记录:明确记录通知顺序、线程安全保证等契约
在大型项目中,我通常会创建一个专门的EventBus类来集中管理所有观察者关系:
cpp复制class EventBus {
public:
template <typename Event>
using Handler = std::function<void(const Event&)>;
template <typename Event>
struct Subscription {
std::weak_ptr<Handler<Event>> handler;
};
template <typename Event>
[[nodiscard]] std::shared_ptr<Handler<Event>> subscribe(Handler<Event> handler) {
auto ptr = std::make_shared<Handler<Event>>(std::move(handler));
std::unique_lock lock(mutex_);
handlers<Event>().push_back({ptr});
return ptr;
}
template <typename Event>
void publish(const Event& event) {
std::shared_lock lock(mutex_);
auto local_copy = handlers<Event>();
lock.unlock();
for (auto& sub : local_copy) {
if (auto handler = sub.handler.lock()) {
(*handler)(event);
}
}
}
private:
template <typename Event>
using SubscriptionList = std::vector<Subscription<Event>>;
template <typename Event>
SubscriptionList<Event>& handlers() {
static std::unordered_map<std::type_index, std::any> handler_map;
const std::type_index type = typeid(Event);
if (auto it = handler_map.find(type); it != handler_map.end()) {
return std::any_cast<SubscriptionList<Event>&>(it->second);
}
auto [it, _] = handler_map.emplace(type, SubscriptionList<Event>{});
return std::any_cast<SubscriptionList<Event>&>(it->second);
}
mutable std::shared_mutex mutex_;
};
这个EventBus实现支持类型安全的事件处理,自动管理订阅者生命周期,并提供了基本的线程安全保证。在实际项目中,可以根据需要扩展支持优先级、事件过滤等功能。
