观察者模式详解:用 Qt 信号与槽机制深入理解

news/2025/3/22 1:59:35/

引言

你是否曾遇到这样的需求:一个对象的状态发生变化后,希望通知其他对象进行相应的更新?比如:

  • 新闻订阅系统:当新闻发布后,所有订阅者都会收到通知。
  • 股票行情推送:股价变化时,所有关注该股票的投资者都会收到更新信息。
  • Qt 界面开发:按钮被点击时,窗口应该发生某些变化。

这些场景都适用于观察者模式(Observer Pattern)

在本篇文章中,我们不仅讲清楚观察者模式的结构,还会用 Qt 的信号与槽机制 来深入解析,让你真正理解这一模式的奥秘!


1. 什么是观察者模式

观察者模式是一种 一对多 的设计模式,允许多个对象(观察者)监听某个对象(被观察者)的状态变化,并在变化时收到通知。

简单来说:

  • 被观察者(Subject):负责维护一个观察者列表,并在状态发生变化时通知所有观察者。
  • 观察者(Observer):接收被观察者的通知并做出相应反应。

现实例子:

场景被观察者(Subject)观察者(Observer)
微信公众号订阅公众号订阅的用户
股票市场股票关注股票的投资者
UI 界面按钮点击按钮(QPushButton监听点击的槽函数

2. 观察者模式的结构

观察者模式一般包括以下角色:

  1. Subject(被观察者)

    • 维护一个观察者列表(即谁在关注它)。
    • 当自身状态发生变化时,通知所有观察者。
  2. Observer(观察者)

    • 订阅 Subject,并实现 update() 方法,接收状态变化通知。
  3. 通知机制

    • 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 + Signal
Observer(观察者)QObject + Slot
通知机制connect()

Qt 实现观察者模式

#include <QObject>
#include <QDebug>// 被观察者(Subject)
class Subject : public QObject {Q_OBJECTsignals:void valueChanged(int newValue);  // 信号(事件)public:void setValue(int value) {emit valueChanged(value);  // 触发信号,通知观察者}
};// 观察者(Observer)
class Observer : public QObject {Q_OBJECTpublic 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::QueuedConnection 支持多线程
解除连接需要手动移除观察者disconnect() 轻松解绑

5. 信号与槽让观察者模式更强大

  1. 自动管理连接,避免空指针错误。
  2. 支持跨线程通信,不需要额外的同步机制。
  3. 更灵活:可以在运行时动态连接和解除连接。
  4. 更易读:代码清晰,不需要手动维护 std::vector<Observer*>

6. 观察者模式如何运用到实际开发中?

1. UI 界面更新

  • 当数据变化时,自动更新 UI 界面(如 QLabelQProgressBar)。
QObject::connect(&subject, &Subject::valueChanged, ui->label, &QLabel::setText);

2. 多线程任务通知主线程

  • 通过 Qt::QueuedConnection 让后台线程通知主线程刷新 UI。

3. 事件驱动开发

  • Qt 本身就是事件驱动的,信号与槽大幅减少了回调函数的使用。

7. 总结

  • 观察者模式解决了 对象间的事件通知 问题。
  • Qt 信号与槽机制观察者模式的高级实现,提供自动管理、类型安全、跨线程支持
  • 在 Qt 开发中,几乎所有 UI 组件、后台任务、事件响应都基于 信号与槽

你学会了吗? 🎯 如果有疑问,欢迎讨论! 🚀


http://www.ppmy.cn/news/1581031.html

相关文章

实时监控、数据分析!Web-Check构建你的网站健康检测系统实操方案

文章目录 前言1.关于Web-Check2.功能特点3.安装Docker4.创建并启动Web-Check容器5.本地访问测试6.公网远程访问本地Web-Check7.内网穿透工具安装8.创建远程连接公网地址9.使用固定公网地址远程访问 前言 在数字化运维领域&#xff0c;网站稳定性保障始终是开发者和运维团队的核…

Towards Universal Soccer Video Understanding——论文学习(足球类)

一、主要内容 作为一项享誉全球的运动&#xff0c;足球吸引了世界各地球迷的广泛兴趣。本文旨在为足球视频的理解开发一个全面的多模态框架。具体来说&#xff0c;在本文中做出了以下贡献:(i)引入了SoccerReplay-1988&#xff0c;这是迄今为止最大的多模式足球数据集&#xff0…

【Linux】应用层自定义协议 + 序列化和反序列化

应用层自定义协议 序列化和反序列化 一.应用层1.再谈 "协议"2.序列化 和 反序列化 二. Jsoncpp1.序列化2.反序列化 三. Tcp全双工 面向字节流四.自定义协议 保证报文的完整性1.Makefile2.Mutex.hpp3.Cond.hpp4.Log.hpp5.Thread.hpp6.ThreadPool.hpp7.Common.hpp8.…

conda、poetry,pip相关

poetry poetry 是一个 Python 打包和依赖管理工具&#xff0c;旨在简化 Python 包的创建、发布和依赖管理。与传统的 setuptools、pip 和 requirements.txt 的组合相比&#xff0c;poetry 提供了一个统一和简化的工具和工作流程。 以下是关于 poetry 的详细介绍&#xff1a; …

Web 小项目: 网页版图书管理系统

目录 最终效果展示 代码 Gitee 地址 1. 引言 2. 留言板 [热身小练习] 2.1 准备工作 - 配置相关 2.2 创建留言表 2.3 创建 Java 类 2.4 定义 Mapper 接口 2.5 controller 2.6 service 3. 图书管理系统 3.1 准备工作 - 配置相关 3.2 创建数据库表 3.2.1 创建用户表…

ChatTTS 开源文本转语音模型本地部署 API 使用和搭建 WebUI 界面

ChatTTS&#xff08;Chat Text To Speech&#xff09;&#xff0c;专为对话场景设计的文本生成语音(TTS)模型&#xff0c;适用于大型语言模型(LLM)助手的对话任务&#xff0c;以及诸如对话式音频和视频介绍等应用。支持中文和英文&#xff0c;还可以穿插笑声、说话间的停顿、以…

使用 Hyperlane 框架的 WebSocket 功能

使用 Hyperlane 框架的 WebSocket 功能 概述 hyperlane 是一个轻量级且高性能的 Rust HTTP 服务器库&#xff0c;支持 HTTP 请求解析、响应构建、TCP 通信&#xff0c;同时也支持 WebSocket 和 SSE 等实时通信协议。hyperlane 框架内置了 WebSocket 支持&#xff0c;能够自动…

Spark 中的Shuffle过程

Shuffle是Spark中一个非常重要的概念&#xff0c;但它也是一个昂贵的操作。以下是对Shuffle过程的详细解释以及它为什么昂贵的原因。 1. 什么是Shuffle&#xff1f; Shuffle是Spark中重新分配数据的过程&#xff0c;通常发生在需要对数据进行重新分组或聚合的操作中&#xff0…