设计模式 Day 5:夯实观察者模式(Boost 实战精讲)

今天我们继续深入观察者模式的学习,不再局限于手写的抽象结构,而是聚焦于真实项目中如何使用成熟框架(如 Boost.Signals2)高效落地观察者模式

本篇采用**“理论解析 + 问答讲解 + 实战用例”**结构,帮助你从设计思想到工程实现,系统理解观察者模式,并掌握在职场中如何高质量表达。


一、观察者模式的核心理论

📌 定义(来自《设计模式》GoF):

**观察者模式(Observer Pattern)**定义对象之间的一对多依赖关系,使得当一个对象状态发生变化时,所有依赖于它的对象都会被自动通知和更新。

✅ 模式动机:

  • 某个对象(Subject)状态改变后,多个依赖对象(Observers)需要自动获取更新。
  • 为避免强耦合与重复代码,采用抽象接口进行解耦。

🧱 角色划分:

角色职责
Subject维护观察者列表,状态变更时通知所有观察者
Observer定义一个更新接口,供主题调用
ConcreteX实现具体主题/观察者逻辑

🔁 通知流程:

  主题状态变化  →  遍历观察者列表  →  调用每个 observer 的 update() 方法

在这里插入图片描述

✅ 应用特征:

  • 主题变化 → 观察者自动联动
  • 低耦合、高扩展性
  • 一对多广播结构,面向事件的典型模型

二、Q1:为什么不能一直手写观察者模式?

✅ 答案:
虽然观察者结构很清晰,但在实际项目中手写版本存在如下问题:

问题影响
增删观察者需手动管理易导致悬空指针、内存泄漏
缺乏线程安全多线程场景下容易数据竞争
生命周期管理复杂观察者被销毁后,主题还在通知 → 崩溃
使用语法繁琐不够直观、不易复用

因此在工程中,我们推荐使用成熟库:

  • boost::signals2
  • Qt signal/slot
  • RxCpp

三、Q2:Boost.Signals2 的核心逻辑框架是怎样的?

✅ 答案:Boost.Signals2 提供了“信号(signal)与槽(slot)”机制,等价于观察者结构:

  • signal = Subject(可连接多个观察者)
  • slot = Observer(绑定的响应函数)

🎯 流程图:

signal<T>::connect(slot函数) → 存储函数指针
→ signal(...) 触发 → 调用所有 slot

✅ 优势机制:

  • 自动解绑:生命周期绑定,防止悬空引用
  • 支持多种 slot 类型:函数指针、lambda、成员函数等
  • 默认线程安全(基于 mutex)
  • 返回值合并器(collectors)

四、Q3:如何用 Boost.Signals2 快速实现“股票通知系统”?

📌 场景:

多个模块订阅价格更新事件,无需关心数据来源。

✅ 实战代码:

#include <iostream>
#include <boost/signals2.hpp>boost::signals2::signal<void(const std::string&, float)> priceChanged;void traderA(const std::string& symbol, float price) {std::cout << "[TraderA] " << symbol << ": " << price << " 元" << std::endl;
}class TraderB {
public:void onPrice(const std::string& symbol, float price) {std::cout << "[TraderB] " << symbol << ": " << price << " 元" << std::endl;}
};int main() {priceChanged.connect(&traderA);TraderB b;priceChanged.connect(boost::bind(&TraderB::onPrice, &b, _1, _2));priceChanged("TSLA", 888.8);priceChanged("AAPL", 175.3);return 0;
}

✅ 输出:

[TraderA] TSLA: 888.8 元
[TraderB] TSLA: 888.8 元
[TraderA] AAPL: 175.3 元
[TraderB] AAPL: 175.3 元

五、Q4:Boost 如何自动解绑观察者?如何避免悬空?

✅ 答案:Boost 提供两种方式:

✅ 方法一:使用 scoped_connection 自动断连

boost::signals2::scoped_connection conn = signal.connect(...);
// 当 conn 离开作用域,自动断开连接

✅ 方法二:用 shared_ptr 绑定观察者对象

std::shared_ptr<TraderB> p = std::make_shared<TraderB>();
signal.connect(boost::bind(&TraderB::onPrice, p, _1, _2));
// 当 p 被释放,观察者自动无效,不再调用

这解决了传统观察者最大的问题之一:“对象被释放但主题还在通知”


六、Q5:常见工程落地场景(强记)

实战场景描述
股票/证券系统客户端订阅行情变化 → 推送更新
硬件温度采集系统温控模块、报警模块订阅传感器数据
插件机制插件监听核心状态或生命周期事件
UI 数据绑定界面组件监听模型变化 → 实时刷新显示
日志 hook 管理注册多个模块日志输出监听,分类处理
游戏对象状态广播血量/坐标变化推送至渲染、AI、动画模块

这些实际场景中,Boost.Signals2 都是优秀的解耦工具。


七、Q6:面试场景问“你用过观察者模式吗?”怎么答最加分?

✅ 标准回答范式:

“我在多个项目中用过观察者模式实现事件解耦,特别是在实时推送系统中使用 Boost.Signals2 实现了模块间的消息广播。相比手写观察者,Boost 提供了线程安全、自动解绑、连接管理的完整机制,比如我们用 scoped_connection 来绑定每个 UI 组件,避免对象销毁时崩溃。”

✅ 加分:结合“具体场景 + 技术细节 + 优劣对比”,突出实战经验。


八、总结记忆

观察者模式是设计模式中应用最广、最接地气的一种。

🎯 理解关键词:

  • 一对多、松耦合、自动通知、广播机制
  • Boost.Signals2 = 安全 + 简洁 + 模块化

✅ 口诀记忆:

“状态一变多方知,信号插槽最优解;解绑安全避崩溃,实战面试皆拿捏。”


明日预告:策略模式(Strategy Pattern)实战讲解:支付/压缩/路径选择等算法切换的优雅实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/77458.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式 Day 3:抽象工厂模式(Abstract Factory Pattern)详解

经过前两天的学习&#xff0c;我们已经掌握了单例模式与工厂方法模式&#xff0c;理解了如何控制实例个数与如何通过子类封装对象的创建逻辑。 今天&#xff0c;我们将进一步深入“工厂”体系&#xff0c;学习抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;&a…

MySQL:事务的理解

一、CURD不加控制&#xff0c;会有什么问题 &#xff08;1&#xff09;因为&#xff0c;MySQL里面存的是数据&#xff0c;所以很有可能会被多个客户访问&#xff0c;所以mysqld可能一次会接受到多个关于CURD的请求。&#xff08;2&#xff09;且mysql内部是采用多线程来完成数…

蓝桥杯刷题--宝石组合

在一个神秘的森林里&#xff0c;住着一个小精灵名叫小蓝。有一天&#xff0c;他偶然发现了一个隐藏在树洞里的宝藏&#xff0c;里面装满了闪烁着美丽光芒的宝石。这些宝石都有着不同的颜色和形状&#xff0c;但最引人注目的是它们各自独特的 “闪亮度” 属性。每颗宝石都有一个…

DAY06:【pytorch】图像增强

1、基本概念 数据增强&#xff0c;又称数据增广、数据扩增&#xff0c;是对训练集进行变换&#xff0c;使训练集更丰富&#xff0c;从而让模型更具泛化能力 2、裁剪 — — Crop 2.1 transforms.CenterCrop 功能&#xff1a;从图像中心裁剪图片 size&#xff1a;所需裁剪图…

mysql 禁止 读 某个 表

mysql 禁止 读 某个 表 mysql禁用某张表,禁用MySQL表的操作 https://shuyeidc.com/wp/89479.html MySQL严格禁止读取表如何避免数据泄露 https://www.kdun.cn/ask/394700.html select host,user from mysql.user; FLUSH PRIVILEGES; 1. MySQL严格禁止读取表如何避免数据泄露…

机器学习 从入门到精通 day_03

1. KNN算法-分类 1.1 样本距离判断 明可夫斯基距离&#xff1a;欧式距离&#xff0c;明可夫斯基距离的特殊情况&#xff1b;曼哈顿距离&#xff0c;明可夫斯基距离的特殊情况。 两个样本的距离公式可以通过如下公式进行计算&#xff0c;又称为欧式距离。 &#xff08;…

LeetCode 热题 100_零钱兑换(85_322_中等_C++)(动态规划)

LeetCode 热题 100_零钱兑换&#xff08;85_322&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;动态规划&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;动态规划&#xff09;&a…

游戏盾IP可以被破解吗

游戏盾IP&#xff08;如上海云盾SDK、腾讯云游戏盾&#xff09;是专为游戏行业设计的高防服务&#xff0c;旨在抵御DDoS攻击、CC攻击等威胁。其安全性取决于​​技术架构、防护能力​​以及​​运维策略​​。虽然理论上没有绝对“无法破解”的系统&#xff0c;但游戏盾IP在合理…

SpringBoot实战1

SpringBoot实战1 一、开发环境&#xff0c;环境搭建-----创建项目 通过传统的Maven工程进行创建SpringBoot项目 &#xff08;1&#xff09;导入SpringBoot项目开发所需要的依赖 一个父依赖&#xff1a;&#xff08;工件ID为&#xff1a;spring-boot-starter-parent&#xf…

【软考-高级】【信息系统项目管理师】【论文基础】进度管理过程输入输出及工具技术的使用方法

定义 项目进度管理是为了保证项目按时完成&#xff0c;对项目中所需的各个过程进行管理的过程&#xff0c;包括规划进度、定义活动、活动优先级排序、活动持续时间、制定进度计划和控制进度。 管理基础 制定进度计划的一般步骤 选择进度计划方法&#xff08;如关键路径法&a…

【Linux】之【Get】 chroot 环境下安装deb包时 .postinst:行 9: 201 段错误 (核心已转储)ldconfig

背景 如题&#xff0c;在postinst文件中直接执行了ldconfig命令&#xff0c; chroot 环境下出错&#xff0c;安装失败 分析 chroot 环境下不能用 ldconfig 和 systemctl 但是&#xff1a;如果环境是 chroot&#xff0c;系统有可能没完整挂载 /proc、/dev、系统路径&#xff…

【论文精读与实现】EDC²-RAG:基于动态聚类的文档压缩方法提升检索增强生成RAG性能

🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创AI未来! 🚀 1. 论文核心思想 这篇由清华大学团队提出的EDC-RAG框架,针对当前…

OSPF接口的网络类型和不规则区域

网络类型(数据链路层所使用的协议所构建的二层网络类型) 1、MA --- 多点接入网络 BMA --- 支持广播的多点接入网络 NBMA --- 不支持广播的多点接入网络 2、P2P --- 点到点网络 以太网 --- 以太网最主要的特点是需要基于MAC地址进行物理寻址&#xff0c;主要是因为以太网接口所连…

HTTP代理:内容分发战场上的「隐形指挥官」

目录 一、技术本质&#xff1a;流量博弈中的「规则改写者」 二、战略价值&#xff1a;内容分发的「四维升级」 三、实战案例&#xff1a;代理技术的「降维打击」 四、未来进化&#xff1a;代理技术的「认知升级」 五、结语&#xff1a;代理技术的「战略觉醒」 在数字内容爆…

(2)网络学习之堡垒机

堡垒机和防火墙的区别&#xff1a; 1.功能定位 防火墙主要负责抵御外部攻击&#xff0c;就像一道坚固的城墙&#xff0c;防止黑客进入内部网络。堡垒机则专注于内部管理&#xff0c;监控和记录运维人员的操作行为&#xff0c;确保内部网络的安全。 2.部署位置与作用范围 防…

minio命令行客户端mc常见用法

安装minio命令行客户端mc https://min-io.cn/docs/minio/linux/reference/minio-mc-admin.html # Windows安装minio命令行客户端 choco install minio-client -y# Linux安装mc客户端 wget -c -P /usr/local/bin/ https://dl.min.io/client/mc/release/linux-amd64/mc # 赋予可…

idea调整控制台日志显示长度

概述 在调试时&#xff0c;idea控制台显示的日志有长度显示&#xff0c;当显示的日志太长时&#xff0c;后生成的日志会覆盖掉之前生成的日志内容。想要调整长度就可以按以下方式进行设置。 设置方法 Settings -> Editor -> General -> Console -> Override con…

oracle em修复之路

很早以前写的文章&#xff0c;再草稿中存放太久了&#xff0c;今天开始整理20年来工作体会&#xff0c;以后陆续发出&#xff0c;希望给大家提供小小的帮助。 去年做的项目使用的oracle数据库&#xff0c;最近要看一下&#xff0c;启动机器进入系统&#xff0c;出现无法加载数…

QT中怎么隐藏或显示最大化、最小化、关闭按钮

文章目录 方法一&#xff1a;通过代码动态设置1、隐藏最大化按钮2、隐藏最小化按钮3、隐藏关闭按钮方法 1&#xff1a;移除 WindowCloseButtonHint方法 2&#xff1a;使用 Qt::CustomizeWindowHint 并手动控制按钮 4、同时隐藏最大化和最小化按钮5、同时隐藏最大化和关闭按钮6、…

性能比拼: Redis vs Memcached

本内容是对知名性能评测博主 Anton Putra Redis vs Memcached Performance Benchmark 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 在本视频中&#xff0c;我们将对比 Redis 和 Memcached。我会介绍一些功能上的不同&#xff0c;但主要关注 性能。 首先&#xf…