弁言
你是否曾碰到如许的需求:一个对象的状态发生变革后,盼望通知其他对象进行相应的更新?好比:
- 新闻订阅系统:当新闻发布后,所有订阅者都会收到通知。
- 股票行情推送:股价变革时,所有关注该股票的投资者都会收到更新信息。
- Qt 界面开发:按钮被点击时,窗口应该发生某些变革。
这些场景都适用于观察者模式(Observer Pattern)。
在本篇文章中,我们不仅讲清楚观察者模式的结构,还会用 Qt 的信号与槽机制 来深入剖析,让你真正明白这一模式的奥秘!
1. 什么是观察者模式?
观察者模式是一种 一对多 的计划模式,答应多个对象(观察者)监听某个对象(被观察者)的状态变革,并在变革时收到通知。
简单来说:
- 被观察者(Subject):负责维护一个观察者列表,并在状态发生变革时通知所有观察者。
- 观察者(Observer):吸收被观察者的通知并做出相应反应。
现实例子:
场景被观察者(Subject)观察者(Observer)微信公众号订阅公众号订阅的用户股票市场股票关注股票的投资者UI 界面按钮点击按钮(QPushButton)监听点击的槽函数 2. 观察者模式的结构
观察者模式一样平常包括以下脚色:
- Subject(被观察者):
- 维护一个观察者列表(即谁在关注它)。
- 当自身状态发生变革时,通知所有观察者。
- Observer(观察者):
- 订阅 Subject,并实现 update() 方法,吸收状态变革通知。
- 通知机制:
- Subject 需要提供 attach() 和 notify() 方法,管理观察者并进行通知。
观察者模式 UML 结构图
- +-------------+ +----------------+
- | Observer |<------| Subject |
- +-------------+ +----------------+
- | +update() | | +attach() |
- | | | +detach() |
- | | | +notify() |
- +-------------+ +----------------+
复制代码 3. 传统 C++ 实现观察者模式
在 C++ 中,我们可以用 vector 存储观察者列表,并手动通知它们:
传统 C++ 实现
- #include <iostream>
- #include <vector>
- // 观察者接口
- class Observer {
- public:
- virtual void update(int value) = 0;
- };
- // 被观察者(Subject)
- class Subject {
- private:
- std::vector<Observer*> observers;
- int state;
- public:
- void attach(Observer* observer) { observers.push_back(observer); }
- void notify() {
- for (Observer* obs : observers) {
- obs->update(state);
- }
- }
- void setState(int value) {
- state = value;
- notify();
- }
- };
- // 具体观察者
- class ConcreteObserver : public Observer {
- public:
- void update(int value) override {
- std::cout << "Observer received update: " << value << std::endl;
- }
- };
- int main() {
- Subject subject;
- ConcreteObserver observer1, observer2;
- subject.attach(&observer1);
- subject.attach(&observer2);
- subject.setState(42); // 触发通知
- }
复制代码 题目:
- 需要手动管理 Observer 的列表。
- notify() 需要手动遍历所有观察者,不够机动。
- 可能会出现空指针题目(被观察者销毁后,观察者仍在利用)。
4. 用 Qt 信号与槽实现观察者模式
Qt 提供了一种更强大、更安全的实现方式——信号(Signal)与槽(Slot)机制。它本质上就是观察者模式的扩展,但更加机动和易用!
信号与槽怎样实现观察者模式?
观察者模式脚色Qt 信号与槽对应Subject(被观察者)QObject + SignalObserver(观察者)QObject + Slot通知机制connect() Qt 实现观察者模式
- #include <QObject>
- #include <QDebug>
- // 被观察者(Subject)
- class Subject : public QObject {
- Q_OBJECT
- signals:
- void valueChanged(int newValue); // 信号(事件)
- public:
- void setValue(int value) {
- emit valueChanged(value); // 触发信号,通知观察者
- }
- };
- // 观察者(Observer)
- class Observer : public QObject {
- Q_OBJECT
- public slots:
- void onValueChanged(int newValue) {
- qDebug() << "Observer received new value:" << newValue;
- }
- };
- int main() {
- Subject subject;
- Observer observer;
- // 连接信号和槽
- QObject::connect(&subject, &Subject::valueChanged, &observer, &Observer::onValueChanged);
- // 触发事件
- subject.setValue(42);
- }
复制代码 Qt 信号与槽 vs 传统观察者模式
对比项传统观察者模式Qt 信号与槽观察者管理方式需手动维护列表主动管理触发方式直接调用 update()emit 发送信号线程安全需要手动同步Qt: ueuedConnection 支持多线程排除毗连需要手动移除观察者disconnect() 轻松解绑 5. 信号与槽让观察者模式更强大
- 主动管理毗连,克制空指针错误。
- 支持跨线程通信,不需要额外的同步机制。
- 更机动:可以在运行时动态毗连和排除毗连。
- 更易读:代码清晰,不需要手动维护 std::vector<Observer*>。
6. 观察者模式怎样运用到实际开发中?
1. UI 界面更新
- 当数据变革时,主动更新 UI 界面(如 QLabel、QProgressBar)。
- QObject::connect(&subject, &Subject::valueChanged, ui->label, &QLabel::setText);
复制代码 2. 多线程使命通知主线程
- 通过 Qt:
ueuedConnection 让后台线程通知主线程革新 UI。
3. 事件驱动开发
- Qt 本身就是事件驱动的,信号与槽大幅淘汰了回调函数的利用。
7. 总结
- 观察者模式解决了 对象间的事件通知 题目。
- Qt 信号与槽机制 是观察者模式的高级实现,提供主动管理、类型安全、跨线程支持。
- 在 Qt 开发中,几乎所有 UI 组件、后台使命、事件响应都基于 信号与槽。
你学会了吗? |