c++实现观察者模式(附带源码)
C++ 实现观察者模式项目详解
项目背景详细介绍
在软件开发过程中,“观察者模式”是一种非常经典的设计模式,属于行为型设计模式之一。其核心思想是:当一个对象的状态发生变化时,所有依赖于它的对象都能得到通知并自动更新。
这种模式的应用场景非常广泛,例如:
-
GUI 框架中,一个按钮被点击时,所有注册的监听器都能收到通知;
-
游戏引擎中,当角色血量发生变化时,UI 自动更新;
-
多模块系统中,当核心数据发生变化时,相关模块自动感知变化;
-
股票行情推送系统中,多个终端订阅同一只股票的价格变动;
-
消息总线或事件总线系统中,实现低耦合的模块通信。
如果没有观察者模式,我们往往需要手动在每个依赖模块中写很多重复的状态同步代码,模块之间会产生强耦合,维护成本非常高。而观察者模式通过 一对多的消息通知机制,很好地解耦了“发布者”和“订阅者”之间的依赖关系。
本项目的目标是:使用 C++ 编程语言,从零实现一个可复用、可扩展的观察者模式框架,并通过实例演示它在实际开发场景中的应用。
项目需求详细介绍
本项目的功能目标如下:
-
实现基本的观察者模式结构
-
定义“观察者”接口(Observer)
-
定义“被观察者”或“主题”(Subject)
-
被观察者可以注册(Attach)、移除(Detach)、通知(Notify)观察者。
-
-
实现一对多通知机制
-
一个 Subject 可以同时拥有多个 Observer。
-
当 Subject 状态发生变化时,会自动通知所有已注册的观察者。
-
-
实现观察者响应逻辑
-
不同观察者可以实现自己的
update()逻辑。 -
当接收到通知时,各自根据自身业务逻辑执行动作。
-
-
实现订阅/取消订阅机制
-
观察者可以动态注册和移除。
-
支持运行时动态增删观察者,不影响系统整体运行。
-
-
实现简单的演示案例
-
使用一个简单的“状态变化”场景来演示观察者模式。
-
比如:一个数据源状态变化时,多个 UI 元素或模块同时收到通知。
-
相关技术详细介绍
在本项目中,我们会用到以下关键技术和 C++ 编程知识点:
1. 面向对象编程思想(OOP)
观察者模式的基础是面向对象。我们通过接口(纯虚类)定义观察者和被观察者的行为。观察者只需实现接口中的 update() 方法即可接收通知。
2. C++ 虚函数与多态机制
我们通过 C++ 虚函数来实现动态绑定,使不同观察者对象在接收到通知时,能够调用自己对应的逻辑。这也是观察者模式实现的关键之一。
3. 容器与迭代器(std::vector)
被观察者内部通过容器(如 std::vector)保存所有观察者指针,实现动态的注册与移除功能。
4. 标准库算法(std::remove + erase)
在移除观察者时使用 erase-remove 惯用法,提高代码简洁性和可维护性。
5. 解耦与扩展性
观察者模式的设计目标之一就是 降低模块耦合度。
发布者(Subject)不关心观察者的具体类型,也不依赖于它们的实现,从而提高系统的灵活性。
实现思路详细介绍
1. 定义 Observer 接口
我们首先定义一个抽象类(纯虚类)IObserver,其中包含一个纯虚函数 update(int state),用于接收被观察者状态变化时的通知。
2. 定义 Subject 类
Subject 作为被观察者,包含:
-
std::vector<IObserver*> observers保存所有观察者; -
attach()注册观察者; -
detach()移除观察者; -
notify()通知所有观察者; -
setState()修改状态并触发通知。
3. 定义具体的观察者类
通过继承 IObserver,我们可以创建多个具体观察者类。每个观察者在 update() 中实现自己收到通知后的反应逻辑。
4. 触发状态变化
当 Subject 状态发生变化时,调用 notify(),遍历所有注册的观察者,并依次调用 update() 方法,从而实现一对多的消息广播。
5. 演示功能
在 main() 函数中,创建一个 Subject 对象和多个 ConcreteObserver 对象。
注册观察者后,修改状态,观察输出结果。
完整实现代码
/*****************************************************
* 文件名称: observer_pattern.cpp
* 项目功能: 使用 C++ 实现观察者模式(Observer Pattern)
* 适用场景: GUI事件监听、消息广播、状态变化通知
* 作者: ChatGPT
* 编译环境: C++17
*****************************************************/
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
/*****************************************************
* 抽象观察者接口类
* 每个观察者都需要实现 update() 方法,用于接收通知
*****************************************************/
class IObserver {
public:
virtual ~IObserver() = default;
// state 代表被观察者的最新状态
virtual void update(int state) = 0;
};
/*****************************************************
* 被观察者(Subject)
* 负责维护观察者列表、通知观察者
*****************************************************/
class Subject {
public:
// 添加观察者
void attach(IObserver* observer) {
observers_.push_back(observer);
}
// 移除观察者
void detach(IObserver* observer) {
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
}
// 设置状态并通知所有观察者
void setState(int newState) {
state_ = newState;
notify();
}
// 获取当前状态
int getState() const {
return state_;
}
private:
// 通知所有观察者
void notify() {
for (auto* obs : observers_) {
if (obs) obs->update(state_);
}
}
private:
int state_ = 0; // 被观察者状态
std::vector<IObserver*> observers_; // 观察者列表
};
/*****************************************************
* 具体观察者类
* 不同观察者可以实现不同的 update() 行为
*****************************************************/
class ConcreteObserver : public IObserver {
public:
explicit ConcreteObserver(std::string name) : name_(std::move(name)) {}
void update(int state) override {
std::cout << "[观察者 " << name_ << "] 收到通知:状态变为 " << state << std::endl;
}
private:
std::string name_;
};
/*****************************************************
* main函数:演示观察者模式的基本用法
*****************************************************/
int main() {
// 创建被观察者对象
Subject subject;
// 创建两个观察者
ConcreteObserver observerA("A");
ConcreteObserver observerB("B");
// 注册观察者
subject.attach(&observerA);
subject.attach(&observerB);
// 修改状态,所有观察者都会收到通知
std::cout << "第一次状态更新为 10:" << std::endl;
subject.setState(10);
// 移除一个观察者
subject.detach(&observerB);
// 再次修改状态,只有 A 会收到通知
std::cout << "\n第二次状态更新为 20:" << std::endl;
subject.setState(20);
return 0;
}
代码详细解读
| 代码部分 | 功能说明 |
|---|---|
IObserver 接口 |
定义观察者的统一行为标准,确保所有观察者都能响应 update() 通知 |
Subject 类 |
保存观察者列表、状态信息,提供注册、移除和广播通知功能 |
attach() 方法 |
将观察者加入列表,实现订阅 |
detach() 方法 |
从列表中移除观察者,实现取消订阅 |
setState() 方法 |
修改状态,并自动调用 notify() 通知所有观察者 |
notify() 方法 |
遍历所有观察者并调用 update(),实现一对多广播 |
ConcreteObserver 类 |
具体观察者实现 update(),打印接收到的状态变化 |
main() 函数 |
创建 Subject 与多个观察者,模拟实际运行场景 |
项目详细总结
本项目通过简洁清晰的 C++ 代码,实现了经典的观察者设计模式。其主要特点包括:
-
一对多通知机制
当 Subject 状态发生变化时,可以同时通知多个 Observer。 -
松耦合结构
Subject 与 Observer 之间仅通过抽象接口通信,彼此不依赖具体实现类,提高了扩展性与可维护性。 -
灵活扩展
可以轻松新增不同类型的观察者,而不必修改 Subject 的代码,符合“开闭原则”。 -
代码结构清晰
使用纯虚类、容器和标准算法实现,逻辑清晰易懂,适合作为项目基础架构的事件机制。
项目常见问题及解答
Q1:观察者对象销毁后忘记移除,会发生什么?
如果 Subject 仍持有已销毁观察者的指针,后续通知会访问非法内存,导致程序崩溃。
✅ 解决方案:
-
手动在观察者销毁前调用
detach(); -
或使用智能指针 / 弱引用管理生命周期(可在扩展优化中实现)。
Q2:一个观察者能同时订阅多个 Subject 吗?
可以。观察者可以注册到多个 Subject 上,当任意一个 Subject 状态变化时,观察者都会收到对应的通知。
Q3:如果观察者内部在 update() 中修改了 Subject,会不会造成死循环?
可能会。比如 update() 里再次调用 setState(),从而导致再次 notify()。
✅ 建议在设计时防止递归通知,比如增加状态检查、事件队列或标志位。
Q4:线程安全问题怎么处理?
上面的实现没有加锁,多线程环境下可能出现竞态问题。
✅ 可在扩展方向中使用 std::mutex 加锁,保证 attach/detach/notify 的线程安全。
扩展方向与性能优化
-
加入智能指针管理
使用std::weak_ptr和std::shared_ptr替代裸指针,自动管理观察者生命周期,避免悬空指针。 -
支持 lambda / function 回调
通过std::function+std::unordered_map可以实现无侵入式订阅机制,方便灵活。 -
支持多线程安全
使用std::mutex或读写锁保证attach()、detach()、notify()线程安全。 -
支持事件过滤
给每个观察者添加感兴趣的事件类型,实现更精细的订阅机制(如 GUI 系统中)。 -
支持订阅 token 自动注销
通过 RAII(Resource Acquisition Is Initialization)技术,在观察者生命周期结束时自动退订,降低手动管理成本。 -
封装为通用事件总线
将本项目扩展为通用 EventBus 或 MessageBus,可用于大型应用架构中,实现模块间通信。
✅ 总结:
本项目完整演示了 C++ 实现观察者模式的全过程,从背景、需求到实现和扩展,结构清晰、逻辑严谨。
观察者模式作为设计模式中的基础模块,在实际工程中具有非常高的应用价值,掌握并灵活运用这一模式,可以大大提高系统的可维护性与扩展性。
更多推荐
所有评论(0)