设计模式 Day 6:深入讲透观察者模式(真实场景 + 回调机制 + 高级理解)

观察者模式(Observer Pattern)是一种设计结构中最实用、最常见的行为模式之一。它的魅力不仅在于简洁的“一对多”事件推送能力,更在于它的解耦能力、模块协作设计、实时响应能力

本篇作为 Day 6,将带你从理论、底层机制到真实工程项目实战,全方位、系统地掌握观察者模式,彻底吃透其设计价值。


一、重新理解观察者模式的本质

✅ 一句话总结:

观察者模式的核心,是在被观察者状态变化时通知所有关心它的对象,从而构建一个低耦合、响应式的通知机制

📌 抽象结构:

class Subject {
public:void attach(Observer* obs);void detach(Observer* obs);void notify();
};class Observer {
public:virtual void update() = 0;
};

🎯 优点:

  • 一对多广播机制,通知多模块
  • 实现了“订阅/发布”架构模型
  • 观察者动态添加移除,系统更灵活

❗ 本质关键:

  • 状态变更感知 + 自动联动更新
  • 利用回调函数机制(函数指针 / lambda)实现通知解耦

二、现实中典型的观察者模式场景(高频 + 好记 + 实战)

在这里插入图片描述

🌟 1. GUI 事件响应系统(经典)

  • 鼠标点击按钮 → 所有监听该按钮事件的 UI 模块立刻响应

✅ 示例:

button.onClick().connect([]() {std::cout << "按钮被点击,弹窗显示!" << std::endl;
});

🌟 2. 实时行情推送系统(金融/交易所)

  • 价格更新后 → 各个终端(交易页面、预警模块、图表组件)即时响应

✅ 模块划分:

  • MarketDataFeed:行情中心(Subject)
  • ChartUI / Alarm / StrategyModel:观察者

🌟 3. 硬件驱动数据采集(IoT / 医疗监护)

  • 心率变化 → 推送给监护仪屏幕、记录系统、告警系统

🌟 4. 游戏开发:对象状态广播

  • 血量变化 → 渲染模块、AI判断模块、音效模块需同时更新

🌟 5. 插件系统事件通知

  • IDE 插件监听工程加载事件、文件保存事件等

三、观察者模式的核心:回调函数机制

观察者的关键,就是通过函数指针 / 回调函数 / lambda 表达式连接行为与状态变化

✅ 1. 函数指针的基本形式

void (*callback)(int);
callback = myHandler;
callback(123); // 执行

✅ 2. lambda 表达式(现代写法)

auto callback = [](int price) {std::cout << "新价格:" << price << std::endl;
};

✅ 3. std::function + std::bind(成员函数回调)

class Alarm {
public:void trigger(int v) { std::cout << "触发报警:" << v << std::endl; }
};Alarm alarm;
std::function<void(int)> cb = std::bind(&Alarm::trigger, &alarm, std::placeholders::_1);
cb(90);

这些函数式调用,正是观察者通知机制的底层实现核心。


四:Boost.Signals2 的使用原理与机制

Boost.Signals2 是观察者模式在现代 C++ 中的高效、安全实现。它将“通知发布者(Subject)”与“通知接收者(Observer)”的注册、解绑、调用机制封装得更加通用和安全。

✅ 基本原理:

  • signal<T> 类似“事件总线”,可连接多个“响应槽(slot)”
  • connect() 注册 slot
  • operator() 触发信号,自动调用所有 slot

✅ 特点分析:

特性说明
自动解绑支持 scoped_connection、生命周期追踪(weak_ptr)
多线程安全所有操作加锁,适合多线程信号触发
返回值聚合器可对多个 slot 的返回值做统一处理(例如返回第一个、合并结果)
插槽灵活连接支持函数、lambda、成员函数、functor 对象

✅ 例子说明:

boost::signals2::signal<void(int)> sig;sig.connect([](int v) { std::cout << "值为:" << v << std::endl; });sig(42); // 触发所有观察者响应

✅ 自动解绑:

{boost::signals2::scoped_connection conn = sig.connect(...);// conn 析构自动解除绑定
} // 安全退出,不再触发此 slot

✅ 成员函数绑定:

sig.connect(boost::bind(&Class::method, &obj, _1));

Boost.Signals2 的底层结构包含:

  • slot 链表容器:存储所有观察者
  • 线程锁保护:对 connect/emit 操作加锁
  • 断开机制:支持连接手动断开、生命周期关联解绑

它解决了手写观察者中最容易出现的问题:

  • 悬空指针访问
  • 多线程数据竞争
  • 连接管理混乱

因此在现代 C++ 项目中,如果你需要一种安全、可维护、低耦合、线程友好的观察者实现方案,Boost.Signals2 是首选。

五、项目实战:构建一个“传感器驱动 + 多模块响应系统”

📌 需求背景:

工业设备上连接温度传感器,当温度变化时,需要:

  • 屏幕显示实时温度
  • 超过阈值时报警
  • 自动记录进系统日志

🎯 类结构图:

+--------------------+
|  TemperatureSensor |
+--------------------+
| +addListener()     | ← 注册观察者
| +removeListener()  |
| +updateTemp()      | ← 状态变化
| +notify()          |
+--------------------+↓多个监听回调函数(Slot)

✅ C++ 实现(使用 Boost.Signals2):

#include <iostream>
#include <boost/signals2.hpp>class TemperatureSensor {
public:boost::signals2::signal<void(float)> onTempChanged;void updateTemp(float newTemp) {std::cout << "[Sensor] 当前温度:" << newTemp << std::endl;onTempChanged(newTemp);  // 触发信号,通知所有观察者}
};class LCDDisplay {
public:void show(float t) {std::cout << "[LCD] 显示温度:" << t << std::endl;}
};class AlarmModule {
public:void check(float t) {if (t > 80)std::cout << "[Alarm] 温度过高,发出警报!" << std::endl;}
};class Logger {
public:void log(float t) {std::cout << "[Log] 记录温度值:" << t << std::endl;}
};int main() {TemperatureSensor sensor;LCDDisplay lcd;AlarmModule alarm;Logger logger;sensor.onTempChanged.connect([&](float t){ lcd.show(t); });sensor.onTempChanged.connect([&](float t){ alarm.check(t); });sensor.onTempChanged.connect([&](float t){ logger.log(t); });sensor.updateTemp(65.0);sensor.updateTemp(88.2);return 0;
}

✅ 输出示例:

[Sensor] 当前温度:65
[LCD] 显示温度:65
[Log] 记录温度值:65
[Sensor] 当前温度:88.2
[LCD] 显示温度:88.2
[Alarm] 温度过高,发出警报!
[Log] 记录温度值:88.2

六、观察者模式的扩展与变种

✅ 延迟通知 vs 立即通知

  • 有些系统不立即通知,而是打包合并,异步发送 → 对应“批量广播机制”

✅ 支持过滤的观察者(按条件触发)

if (t > 100) observerA();
else observerB();

✅ 支持优先级注册

  • 有些响应函数必须先执行,可加入优先级队列(boost 支持插槽分组)

七、面试与复述技巧

“我们项目中大量使用观察者机制来进行模块间解耦,比如设备数据变化后推送到 UI、日志、预警等模块。为了安全性与性能,我们使用了 Boost.Signals2,实现了自动连接管理与线程安全的广播机制,同时通过 lambda 与 bind 配合,保持代码灵活、结构清晰。”

✅ 加分关键词:事件推送 / 多模块联动 / 自动解绑 / 异步广播 / 回调链路


八、总结记忆要点

模块要素说明
Subject状态持有者,触发变化
Observer回调函数实体,响应变化
通知机制connect → 回调列表 → notify 调用
解耦点无需知道观察者是谁,只要通知
实现方式函数指针 / lambda / bind / signal

✅ 一句话背诵版:

“观察者模式通过回调机制建立一对多解耦通道,实现状态联动与模块协作。”


明日预告:Day 7

策略模式(Strategy Pattern)实战详解:在支付系统、路径规划、压缩算法中优雅切换策略。

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

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

相关文章

文献总结:AAAI2025-UniV2X-End-to-end autonomous driving through V2X cooperation

UniV2X 一、文章基本信息二、文章背景三、UniV2X框架1. 车路协同自动驾驶问题定义2. 稀疏-密集混合形态数据3. 交叉视图数据融合&#xff08;智能体融合&#xff09;4. 交叉视图数据融合&#xff08;车道融合&#xff09;5. 交叉视图数据融合&#xff08;占用融合&#xff09;6…

2025蓝桥杯python A组题解

真捐款去了&#xff0c;好长时间没练了&#xff0c;感觉脑子和手都不转悠了。 B F BF BF 赛时都写假了&#xff0c; G G G 也只写了爆搜。 题解其实队友都写好了&#xff0c;我就粘一下自己的代码&#xff0c;稍微提点个人的理解水一篇题解 队友题解 B 思路&#xff1a; 我…

免费送源码:Java+ssm+MySQL 校园二手书销售平台设计与实现 计算机毕业设计原创定制

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对校园二手书销售平台等问题&#xff0c;对校…

工业科学级天文相机:跨界融合的高精密成像解决方案

随着国内科技的快速发展&#xff0c;工业相机领域正悄然兴起一场"天文级"的技术革命。这类兼具工业设备可靠性与天文观测精度的特殊相机&#xff0c;正在半导体制造、天文观测、空间探测等领域开辟新的应用疆域。其核心技术突破不仅体现在传感器性能的提升&#xff0…

论文阅读笔记——Multi-Token Attention

MTA 论文 在 Transformer 中计算注意力权重时&#xff0c;仅依赖单个 Q 和 K 的相似度&#xff0c;无法有效捕捉多标记组合信息。&#xff08;对于 A、B 两个词&#xff0c;单标记注意力需要分别计算两个词的注意力分数&#xff0c;再通过后处理定位共同出现的位置或通过多层隐…

301.找出3位偶数

2094. 找出 3 位偶数 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<Integer> resnew ArrayList<>();List<Integer> linew ArrayList<>();public int[] findEvenNumbers(int[] digits) {Arrays.sort(digits);boolean[] numsnew boolea…

【KWDB 创作者计划】第二卷:开发者实战篇

​KWDB技术白皮书卷二&#xff1a;开发者实战篇 ​1. 自然语言到量子查询的编译系统 1.1 NL2QSQL翻译引擎架构 运行时流程图解&#xff1a; ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────…

前端工程化之新晋打包工具

新晋打包工具 新晋打包工具前端模块工具的发展历程分类初版构建工具grunt使用场景 gulp采用管道机制任务化配置与api简洁 现代打包构建工具基石--webpack基于webpack改进的构建工具rollup 推荐举例说明package.jsonrollup.config.mjsmy-extract-css-rollup-plugin.mjssrc/index…

ai软件UI自动化

在AI与UI自动化结合的场景中,通常涉及计算机视觉(CV)、自然语言处理(NLP)和机器学习(ML)等技术。以下是实现AI驱动UI自动化的关键方向、工具和步骤: ‌一、核心应用场景‌ ‌元素定位增强‌ ‌问题‌:传统工具依赖XPath/CSS选择器,易因UI变化失效。‌AI方案‌:CV识别…

关于 C++ 中 cin 对象和 EOF 的详细解释

【DeepSeek提问】 给解释一下下面这段话&#xff08;C编程&#xff09; cin是 iostream 类的一个对象实例&#xff0c;如果输入正常&#xff0c; cin 将返回本身。 举个例子&#xff1a;cin>x>>y, 如果 cin>>x 读入正常&#xff0c;那么将返回cin, 相当于后面继…

Vue 3 和 Vue 2 的区别及优点

Vue.js 是一个流行的 JavaScript 框架&#xff0c;广泛用于构建用户界面和单页应用。自 Vue 3 发布以来&#xff0c;很多开发者开始探索 Vue 3 相较于 Vue 2 的新特性和优势。Vue 3 引入了许多改进&#xff0c;优化了性能、增强了功能、提升了开发体验。本文将详细介绍 Vue 2 和…

【特权FPGA】之UART串口

0.简介 通用异步收发器(Universal Asynchronous Receiver&#xff0f;Transmitter&#xff0c;UART)可以和各种标准串行接口&#xff0c;如RS 232和RS 485等进行全双工异步通信&#xff0c;具有传输距离远、成本低、可靠性高等优点。一般UART由专用芯片如8250&#xff0c;1645…

Vue3中watch监视reactive对象方法详解

在Vue3中&#xff0c;使用watch监视reactive对象时&#xff0c;需根据监视的目标选择合适的方法。以下是详细的步骤和说明&#xff1a; 1. 监视整个reactive对象 自动深度监视&#xff1a;直接监视reactive对象时&#xff0c;Vue3会默认启用深度监视&#xff0c;无需设置deep:…

如何制定性能调优策略

目录 性能测试攻略 微基准性能测试 宏基准性能测试 热身问题 多 JVM 情况下的影响 合理分析结果&#xff0c;制定调优策略 推荐阅读 性能测试攻略 性能测试是提前发现性能瓶颈&#xff0c;保障系统性能稳定的必要措施。下面我先给你介绍两种常用 的测试方法&#xff0c;帮…

HarmonyOS-ArkUI V2装饰器@Local装饰器:组件内部状态

@Local装饰器的作用 @Local装饰器是用来装饰组件内的状态的。而且它修饰的变量可以成为数据源。Local装饰器,作用跟名字差不多,重点突出了“本地”的特性,也就是使用的范围仅仅限制在组件内部。且它在初始化的时候必须是在本地进行初始化的,不能在外部组件,同时也禁止了外…

Linux线程属性与多线程开发:API详解与实战代码解析

Linux 线程的属性 线程池 多线程的创建 线程的属性 引入 我们设想一个场景&#xff0c;使用pthread_detach时&#xff0c;发现线程早就已经结束了&#xff0c;这时候pthread_detach还能正常发挥清理线程的 独有空间 的作用吗&#xff1f; 答案是可以的&#xff0c;但是这难…

测试第二课-------测试分类

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

MySQL安装实战分享

一、在 Windows 上安装 MySQL 1. 下载 MySQL 安装包 访问 MySQL 官方下载页面。选择适合你操作系统的版本。一般推荐下载 MySQL Installer。 2. 运行安装程序 双击下载的安装文件&#xff08;例如 mysql-installer-community-<version>.msi&#xff09;。如果出现安全…

数据库预热

介绍 Database Warm-up &#x1f9e0; 一句话理解 数据库是在应用启动阶段&#xff0c;提前建立数据库连接 或 执行轻量 SQL 操作&#xff0c;从而 加快首个请求的响应速度 的一种优化手段 &#x1f3af; 为什么需要数据库预热&#xff1f; 当 FastAPI 或其他 Web 服务刚启…

SearXNG

SearXNG 什么是 SearXNG &#xff1f;说白了&#xff0c;其实就是一个免费开源的搜索引擎。那为什么要本地安装它呢&#xff1f; 看它官网的解释(翻译)&#xff0c;当然&#xff0c;其中官方也有一篇文档解释了为什么需要部署使用私有示例&#xff1a;为什么使用私有实例&…