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

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

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


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

✅ 一句话总结:

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

📌 抽象结构:

cpp 复制代码
class Subject {
public:
    void attach(Observer* obs);
    void detach(Observer* obs);
    void notify();
};

class Observer {
public:
    virtual void update() = 0;
};

🎯 优点:

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

❗ 本质关键:

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

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

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

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

✅ 示例:

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

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

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

✅ 模块划分:

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

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

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

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

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

🌟 5. 插件系统事件通知

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

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

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

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

cpp 复制代码
void (*callback)(int);
callback = myHandler;
callback(123); // 执行

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

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

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

cpp 复制代码
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 对象

✅ 例子说明:

cpp 复制代码
boost::signals2::signal<void(int)> sig;

sig.connect([](int v) { std::cout << "值为:" << v << std::endl; });

sig(42); // 触发所有观察者响应

✅ 自动解绑:

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

✅ 成员函数绑定:

cpp 复制代码
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):

cpp 复制代码
#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 立即通知

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

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

cpp 复制代码
if (t > 100) observerA();
else observerB();

✅ 支持优先级注册

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

七、面试与复述技巧

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

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


八、总结记忆要点

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

✅ 一句话背诵版:

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


明日预告:Day 7

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

相关推荐
怡人蝶梦16 分钟前
Java后端技术栈问题排查实战:Spring Boot启动慢、Redis缓存击穿与Kafka消费堆积
java·jvm·redis·kafka·springboot·prometheus
瓯雅爱分享20 分钟前
MES管理系统:Java+Vue,含源码与文档,实现生产过程实时监控、调度与优化,提升制造企业效能
java·mysql·vue·软件工程·源代码管理
庄小焱1 小时前
设计模式——原型设计模式(创建型)
设计模式
鬼多不菜1 小时前
一篇学习CSS的笔记
java·前端·css
深色風信子1 小时前
Eclipse 插件开发 5.3 编辑器 监听输入
java·eclipse·编辑器·编辑器 监听输入·插件 监听输入
庄小焱1 小时前
设计模式——适配器设计模式(结构型)
设计模式
Blossom.1181 小时前
人工智能在智能健康监测中的创新应用与未来趋势
java·人工智能·深度学习·机器学习·语音识别
shangjg32 小时前
Kafka 如何保证不重复消费
java·分布式·后端·kafka
无处不在的海贼2 小时前
小明的Java面试奇遇之互联网保险系统架构与性能优化
java·面试·架构