設計模式 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.pswp.cn/diannao/78768.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/78768.shtml
英文地址,請注明出處:http://en.pswp.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;為什么使用私有實例&…