观察者模式(Observer Pattern)

C++ 观察者模式(Observer Pattern)

一、模式基础概述

1. 模式定义

观察者模式属于行为型设计模式 ,核心用于建立一对多 对象依赖关系。

当被观察者(目标主题)自身状态发生变更时,系统会自动通知所有已订阅的观察者对象,观察者接收到消息后主动执行自身更新业务逻辑,实现状态联动、事件自动推送,彻底解除事件发起方与事件处理方之间的强耦合。

2. 核心设计思想

  1. 事件解耦:被观察者仅负责状态变更与消息广播,不依赖任何具体观察者
  2. 订阅机制:支持程序运行时动态添加、移除观察者,灵活调整监听对象
  3. 统一通知:一次状态改变,批量推送消息至所有订阅者
  4. 被动响应:观察者无需轮询查询状态,等待主题主动推送通知即可
  5. 遵循设计原则 :符合开闭原则依赖倒置原则,新增监听业务无需修改原有主题代码

3. 四大核心标准角色

角色名称 英文名称 核心职责 C++ 实现规范
抽象主题角色 Subject 定义观察者管理接口:订阅、取消订阅、批量通知 纯虚基类,提供统一订阅与通知虚接口
具体主题角色 ConcreteSubject 维护观察者容器列表,存储自身业务状态,状态变化触发通知 继承抽象主题,封装私有状态变量,实现状态修改与消息推送
抽象观察者角色 Observer 定义统一消息响应更新接口,规范所有观察者行为 纯虚基类,声明update()纯虚更新方法
具体观察者角色 ConcreteObserver 实现更新接口,接收主题推送消息,完成自身专属业务逻辑 继承抽象观察者,重写更新方法,处理差异化业务

4. 观察者模式与发布订阅模式核心区别

对比维度 传统观察者模式 发布-订阅模式
依赖关系 主题与观察者互相感知,存在直接依赖 引入事件总线中间层,发布者与订阅者完全无感知
通信方式 主题直接调用观察者更新方法 消息统一投递至总线,由总线分发推送
耦合程度 中度耦合 完全解耦
适用规模 小型本地事件监听、界面联动 跨模块、跨线程、跨进程大型事件分发
实现复杂度 代码简洁,上手简单 架构复杂,支持多事件分类订阅

二、适用场景与禁用场景

适用场景

  1. 业务存在一对多状态联动,一个数据变更需要同步触发多个业务行为
  2. GUI界面开发:控件点击、窗口状态切换、界面数据实时刷新
  3. 消息推送业务:系统公告、价格变动、配置热更新、告警通知
  4. 游戏开发:角色状态变化、技能触发、BUFF生效、场景事件监听
  5. 日志收集、数据统计、行为埋点等批量事件处理场景
  6. 硬件状态监听、设备上下线、传感器数据实时上报

不适用场景

  1. 事件数量极少、固定不变,无需动态增减监听对象
  2. 业务要求严格控制事件执行顺序,观察者无序通知无法满足
  3. 高频海量事件推送,大量观察者同步执行造成主线程阻塞
  4. 简单函数调用即可实现的轻量联动,过度使用造成代码冗余

三、模式优缺点深度分析

优点

  1. 业务高度解耦:发布逻辑与消费逻辑彻底分离,各自独立开发维护
  2. 拓展性极强:新增观察者无需修改主题源码,直接新增监听类即可
  3. 动态灵活管控:程序运行过程中自由订阅、取消监听,适配多变业务
  4. 消息自动联动:摒弃轮询机制,状态变更即时推送,响应效率更高
  5. 统一事件标准:所有观察者遵循同一更新接口,代码格式统一规范

缺点

  1. 通知顺序不可控:默认按照订阅顺序推送,无法自定义执行优先级
  2. 存在递归死循环风险:观察者更新逻辑内修改主题状态,易造成循环通知
  3. 性能损耗明显:订阅观察者数量过多时,批量通知会造成程序卡顿
  4. 调试排查难度大:事件传递链路较长,异常问题定位流程复杂
  5. 内存风险:未及时取消订阅容易造成对象常驻内存,引发内存泄漏

四、C++ 编码实现核心开发规范

  1. 抽象层统一使用纯虚类 定义接口,基类析构函数必须声明为virtual虚析构,杜绝多态内存泄漏
  2. 具体主题内部使用std::vector存储观察者指针/智能指针,统一管理订阅列表
  3. 状态修改逻辑与消息通知逻辑分离,仅在状态发生实际变更时触发推送
  4. 遍历通知观察者时,优先拷贝订阅列表,避免遍历过程中增删元素引发迭代器失效
  5. 大型项目优先使用std::shared_ptr智能指针管理观察者生命周期,自动回收资源
  6. 多线程并发场景下,订阅、取消订阅、事件通知操作需添加互斥锁保证线程安全
  7. 观察者更新方法内禁止编写耗时阻塞逻辑,耗时业务建议异步抛入线程池执行

五、C++ 基础经典实现(新闻推送案例)

1. 抽象观察者 & 抽象主题

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <memory>
using namespace std;

// 抽象观察者
class Observer
{
public:
    virtual ~Observer() = default;
    // 统一更新接口,接收主题推送消息
    virtual void update(const string& newsInfo) = 0;
};

// 抽象主题
class Subject
{
protected:
    vector<Observer*> observerList;
public:
    virtual ~Subject() = default;
    // 订阅观察者
    virtual void attach(Observer* observer)
    {
        observerList.push_back(observer);
    }
    // 取消订阅
    virtual void detach(Observer* observer)
    {
        for (auto iter = observerList.begin(); iter != observerList.end(); ++iter)
        {
            if (*iter == observer)
            {
                observerList.erase(iter);
                break;
            }
        }
    }
    // 批量通知所有观察者
    virtual void notifyAll(const string& msg)
    {
        // 拷贝容器,防止遍历中修改列表崩溃
        vector<Observer*> tempList = observerList;
        for (auto obs : tempList)
        {
            obs->update(msg);
        }
    }
};

2. 具体被观察者(新闻发布平台)

cpp 复制代码
// 具体主题:新闻发布平台
class NewsSubject : public Subject
{
private:
    string currentNews;
public:
    // 发布新闻,状态变更并推送通知
    void publishNews(const string& news)
    {
        currentNews = news;
        cout << "\n【新闻平台发布新资讯】:" << currentNews << endl;
        notifyAll(currentNews);
    }
};

3. 多种具体观察者

cpp 复制代码
// 邮箱订阅用户
class EmailUser : public Observer
{
private:
    string userName;
public:
    EmailUser(string name) : userName(name) {}
    void update(const string& newsInfo) override
    {
        cout << "邮箱用户[" << userName << "] 收到资讯:" << newsInfo << endl;
    }
};

// 手机短信订阅用户
class SmsUser : public Observer
{
private:
    string phoneNum;
public:
    SmsUser(string phone) : phoneNum(phone) {}
    void update(const string& newsInfo) override
    {
        cout << "短信用户[" << phoneNum << "] 收到资讯:" << newsInfo << endl;
    }
};

// 移动端APP订阅用户
class AppUser : public Observer
{
private:
    string userId;
public:
    AppUser(string id) : userId(id) {}
    void update(const string& newsInfo) override
    {
        cout << "APP用户[" << userId << "] 收到资讯:" << newsInfo << endl;
    }
};

4. 客户端业务调用

cpp 复制代码
int main()
{
    // 创建发布主题
    NewsSubject newsPlatform;

    // 创建各类观察者用户
    EmailUser user1("李四");
    SmsUser user2("15612345678");
    AppUser user3("user001");

    // 全部订阅平台资讯
    newsPlatform.attach(&user1);
    newsPlatform.attach(&user2);
    newsPlatform.attach(&user3);

    // 第一次发布新闻,全员接收
    newsPlatform.publishNews("C++观察者模式实战教程正式上线");

    // 取消短信用户订阅
    newsPlatform.detach(&user2);
    cout << "\n===== 已取消短信用户订阅 =====" << endl;

    // 第二次发布新闻,仅剩余用户接收
    newsPlatform.publishNews("设计模式全套学习资料更新完成");

    return 0;
}

六、实战业务案例:股票价格监听系统

cpp 复制代码
// 股票实体类
class Stock
{
private:
    string stockCode;
    double stockPrice;
public:
    Stock(string code, double price) : stockCode(code), stockPrice(price) {}
    void setPrice(double price) { stockPrice = price; }
    double getPrice() const { return stockPrice; }
    string getCode() const { return stockCode; }
};

// 股票交易市场(具体主题)
class StockMarket : public Subject
{
private:
    Stock stockData;
public:
    StockMarket(string code, double price) : stockData(code, price) {}
    // 股票价格变动
    void changePrice(double newPrice)
    {
        stockData.setPrice(newPrice);
        string msg = "股票[" + stockData.getCode() + "]最新价格:" + to_string(newPrice);
        notifyAll(msg);
    }
};

// 股民观察者
class StockInvestor : public Observer
{
private:
    string investorName;
public:
    StockInvestor(string name) : investorName(name) {}
    void update(const string& msg) override
    {
        cout << "股民[" << investorName << "] 实时接收行情:" << msg << endl;
    }
};

七、C++ 现代优雅写法(函数式轻量观察者)

无需继承类,使用std::function实现极简事件监听,适合快速开发

cpp 复制代码
#include <functional>
#include <unordered_map>
#include <mutex>

// 轻量级事件分发器
template<typename MsgType>
class EventDispatcher
{
private:
    using EventFunc = function<void(const MsgType&)>;
    unordered_map<uint64_t, EventFunc> eventMap;
    mutable mutex mtx;
    uint64_t eventId = 1;
public:
    // 注册监听事件
    uint64_t registerEvent(EventFunc&& func)
    {
        lock_guard<mutex> lock(mtx);
        uint64_t id = eventId++;
        eventMap[id] = move(func);
        return id;
    }
    // 移除监听事件
    void removeEvent(uint64_t id)
    {
        lock_guard<mutex> lock(mtx);
        eventMap.erase(id);
    }
    // 推送事件消息
    void pushEvent(const MsgType& msg)
    {
        lock_guard<mutex> lock(mtx);
        auto tempMap = eventMap;
        for (auto& pair : tempMap)
        {
            if (pair.second) pair.second(msg);
        }
    }
};

// 使用示例
int main()
{
    EventDispatcher<string> dispatcher;
    auto id1 = dispatcher.registerEvent([](const string& info){
        cout << "监听1:" << info << endl;
    });
    auto id2 = dispatcher.registerEvent([](const string& info){
        cout << "监听2:" << info << endl;
    });

    dispatcher.pushEvent("函数式观察者模式执行成功");
    dispatcher.removeEvent(id2);
    dispatcher.pushEvent("仅保留监听1生效");
    return 0;
}

八、C++ 项目开发最佳实践

  1. 项目目录分层规范

    ObserverPattern/
    ├─ ObserverBase.h // 抽象观察者基类
    ├─ SubjectBase.h // 抽象主题基类
    ├─ ConcreteSubject.h // 业务具体主题
    └─ ConcreteObserver.h// 各类业务观察者

  2. 严格区分事件生产者事件消费者,业务逻辑互不侵入

  3. 界面UI场景优先使用传统继承式观察者,后台服务优先使用函数式事件分发

  4. 订阅关系随对象生命周期绑定,对象销毁前必须主动取消订阅

  5. 批量事件通知拆分优先级队列,核心事件优先推送,非核心事件延后执行

  6. 禁止在观察者更新函数中修改主题状态,从源头规避循环通知问题

  7. 日志、监控、告警等通用统一监听业务,封装全局静态事件分发器

九、模式核心总结

观察者模式核心精髓:一对多绑定状态,订阅监听解耦业务,状态变更自动广播

该模式是C++行为型模式中使用频率最高的设计模式之一,广泛应用于界面交互、事件驱动、数据同步、消息推送等场景,依靠订阅-通知机制彻底解决多对象状态联动耦合问题,代码结构清晰、拓展便捷,是实现事件驱动架构的核心基础模式。

相关推荐
Dicky-_-zhang1 小时前
云原生存储与数据库选型实战:从传统数据库到云原生数据库的演进
java·jvm
凝小飞1 小时前
cucumber JAVA 一键部署指南
java·集成测试·模块测试
java修仙传1 小时前
Java 实习日记:断面状态筛选 Bug 修复与对比案例日期过滤优化
java·bug·实习
长谷深风1111 小时前
Java并发编程:线程安全与多线程实战指南【个人八股】
java·安全·线程·进程·juc·并发与并行·上下文切换(性能影响因素)
basketball6161 小时前
C++ 强制类型转换:从 C 风格到 C++ 四大金刚
java·c语言·c++
yyuuuzz1 小时前
谷歌云使用的几个常见注意事项
运维·服务器·网络·安全·web安全·云计算·aws
Fu2067211 小时前
OSPF笔记 OSPF --- 开放式最短路径优先
网络·笔记
Dicky-_-zhang2 小时前
容器网络CNI实战:从零搭建网络插件
java·jvm
Mahir082 小时前
Spring 事务深度解析:核心原理与 12 种事务失效场景全解
java·spring·面试·事务失效