设计模式 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)实战详解:在支付系统、路径规划、压缩算法中优雅切换策略。

相关推荐
zybishe13 分钟前
免费送源码:Java+ssm+MySQL 酒店预订管理系统的设计与实现 计算机毕业设计原创定制
java·大数据·python·mysql·微信小程序·php·课程设计
anlogic1 小时前
Java基础 4.12
java·开发语言
weisian1512 小时前
Java常用工具算法-7--秘钥托管云服务2(阿里云 KMS)
java·安全·阿里云
小马爱打代码2 小时前
设计模式:依赖倒转原则 - 依赖抽象,解耦具体实现
设计模式
Alt.92 小时前
SpringMVC基础二(RestFul、接收数据、视图跳转)
java·开发语言·前端·mvc
寒页_2 小时前
2025年第十六届蓝桥杯省赛真题解析 Java B组(简单经验分享)
java·数据结构·经验分享·算法·蓝桥杯
Koma-forever2 小时前
java设计模式-适配器模式
java·设计模式·适配器模式
Yolo@~3 小时前
SpringBoot无法访问静态资源文件CSS、Js问题
java·spring boot·后端
Jennifer33K3 小时前
IDEA 调用 Generate 生成 Getter/Setter 快捷键
java·ide·intellij-idea
爱的叹息3 小时前
JDK(Java Development Kit)从发布至今所有主要版本 的详细差异、新增特性及关键更新的总结,按时间顺序排列
java·数据库·python