C++观察者模式设计及实现:玩转设计模式的发布-订阅机制

文章目录

前言

搞软件开发,对象间怎么高效"唠嗑"是个核心问题。想象一下,一个对象状态变了,怎么让那些依赖它的兄弟们自动知道并更新?观察者模式(Observer Pattern)就是为解决这个痛点而生的行为设计模式。它定义了一对多的依赖关系,让一个对象(主题)状态变化时,所有依赖它的对象(观察者)都能自动收到通知并刷新。

这模式在现代软件里应用贼广,比如:

  • GUI框架里的事件处理
  • 消息队列和事件总线
  • 订阅-发布系统
  • 实时数据监控和更新

这篇猫哥就深入聊聊C++里观察者模式的门道,从基础定义、传统实现到高级玩法,配上硬核代码示例,让你彻底搞懂怎么玩转它。

观察者模式的核心骨架

观察者模式说白了就这四大金刚:

  1. 主题(Subject):也叫被观察者或发布者。管着一群观察者小弟,负责招人(注册)、踢人(移除)和发通知。
  2. 观察者(Observer):也叫订阅者。定了规矩,主题通知来了得知道怎么接。
  3. 具体主题(Concrete Subject):主题的实干派。自己维护状态,状态一变就吆喝通知观察者。
  4. 具体观察者(Concrete Observer):观察者的行动派。接到通知后知道该干嘛,更新自己状态。

传统实现:手把手搭个观察者

先把架子搭好(抽象接口)

cpp 复制代码
// observer.h
#ifndef OBSERVER_H
#define OBSERVER_H

#include <string>
#include <vector>
#include <memory>

// 观察者接口:定个接收消息的规矩
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const std::string& message) = 0; // 纯虚函数,必须实现
};

// 主题接口:管小弟的招数
class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(std::shared_ptr<Observer> observer) = 0; // 收小弟
    virtual void detach(std::shared_ptr<Observer> observer) = 0; // 踢小弟
    virtual void notify(const std::string& message) = 0;         // 发通知
};

#endif // OBSERVER_H

实干派登场(具体实现)

cpp 复制代码
// concrete_subject.h
#include "observer.h"
#include <mutex>

class ConcreteSubject : public Subject {
private:
    std::string state_; // 核心状态
    std::vector<std::weak_ptr<Observer>> observers_; // 用weak_ptr防循环引用
    mutable std::mutex mutex_; // 线程安全必备

public:
    // 收小弟(线程安全)
    void attach(std::shared_ptr<Observer> observer) override {
        std::lock_guard<std::mutex> lock(mutex_);
        observers_.push_back(observer);
    }

    // 踢小弟(线程安全,清理失效观察者)
    void detach(std::shared_ptr<Observer> observer) override {
        std::lock_guard<std::mutex> lock(mutex_);
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [&observer](const std::weak_ptr<Observer>& w) {
                    auto s = w.lock();
                    return !s || s.get() == observer.get(); // 找失效或指定观察者
                }),
            observers_.end());
    }

    // 发通知(线程安全,边通知边清理僵尸)
    void notify(const std::string& message) override {
        std::lock_guard<std::mutex> lock(mutex_);
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [message](const std::weak_ptr<Observer>& w) {
                    auto s = w.lock();
                    if (s) {
                        s->update(message); // 通知有效观察者
                        return false;       // 保留
                    }
                    return true;            // 标记失效的待清理
                }),
            observers_.end());
    }

    // 改状态,顺便吆喝
    void setState(const std::string& state) {
        state_ = state;
        notify("State changed to: " + state_); // 状态变,通知到!
    }
};

// concrete_observer.h
#include "observer.h"
#include <iostream>

class ConcreteObserver : public Observer {
private:
    std::string name_;
    int notificationCount_ = 0; // 计数收到几次通知

public:
    explicit ConcreteObserver(const std::string& name) : name_(name) {}

    // 接到通知后的动作
    void update(const std::string& message) override {
        notificationCount_++;
        std::cout << "Observer " << name_ << " received: " << message << " (Total: " << notificationCount_ << ")" << std::endl;
    }
};

跑起来看看效果

cpp 复制代码
#include "concrete_subject.h"
#include "concrete_observer.h"
#include <memory>

int main() {
    // 老大登场
    auto subject = std::make_shared<ConcreteSubject>();

    // 两个吃瓜小弟
    auto observer1 = std::make_shared<ConcreteObserver>("Observer1");
    auto observer2 = std::make_shared<ConcreteObserver>("Observer2");

    // 收小弟入伙
    subject->attach(observer1);
    subject->attach(observer2);

    // 老大状态变了,小弟们收通知!
    subject->setState("Active");
    subject->setState("Inactive");

    // 踢走一个
    subject->detach(observer1);

    // 再变状态,只剩一个收通知
    subject->setState("Processing");

    return 0;
}

高级玩法:更强大的观察者模式

传统模式够用但不够酷。复杂系统里,得整点更强大的功能:

  • 多事件类型:不同事发不同通知
  • 事件驱动:基于事件灵活通知
  • 版本追踪:状态变化有迹可循
  • 线程安全:多线程环境稳如狗

事件驱动引擎

cpp 复制代码
// event_manager.h
#ifndef EVENT_MANAGER_H
#define EVENT_MANAGER_H

#include <unordered_map>
#include <vector>
#include <memory>
#include <mutex>
#include <string>

// 定义有啥事件
enum class EventType {
    STATE_CHANGE,  // 状态变
    DATA_UPDATE,   // 数据更新
    SYSTEM_ALERT,  // 系统告警
    USER_ACTION    // 用户操作
};

// 事件包裹里装啥
struct EventData {
    EventType type;
    std::string message;
    std::unordered_map<std::string, std::string> properties; // 额外属性

    EventData(EventType t, const std::string& msg) : type(t), message(msg) {}
};

// 事件观察者:接事件通知
class EventObserver {
public:
    virtual ~EventObserver() = default;
    virtual void onEvent(const EventData& event) = 0;
};

// 事件管家:管事件分发
class EventManager {
private:
    std::unordered_map<EventType, std::vector<std::shared_ptr<EventObserver>>> observers_; // 按事件类型存观察者
    mutable std::mutex mutex_; // 锁住,线程安全

public:
    void subscribe(EventType type, std::shared_ptr<EventObserver> observer); // 订阅事件
    void unsubscribe(EventType type, std::shared_ptr<EventObserver> observer); // 取消订阅
    void notify(const EventData& event); // 发事件通知
};
#endif // EVENT_MANAGER_H

智能主题(传统+事件双修)

cpp 复制代码
// smart_subject.h
#ifndef SMART_SUBJECT_H
#define SMART_SUBJECT_H

#include "observer.h"
#include "event_manager.h"
#include <string>
#include <memory>
#include <chrono>
#include <mutex>

// 智能主题:兼容传统观察者 + 事件驱动
class SmartSubject : public Subject, public std::enable_shared_from_this<SmartSubject> {
private:
    std::string state_; // 状态
    int version_ = 0; // 状态版本号
    std::shared_ptr<EventManager> eventManager_; // 事件管家
    std::chrono::steady_clock::time_point lastUpdate_; // 上次更新时间
    std::vector<std::weak_ptr<Observer>> traditionalObservers_; // 传统小弟
    mutable std::mutex observerMutex_; // 传统小弟的锁

public:
    SmartSubject(); // 构造
    ~SmartSubject() = default;

    // 传统接口:收/踢小弟, 发通知
    void attach(std::shared_ptr<Observer> observer) override;
    void detach(std::shared_ptr<Observer> observer) override;
    void notify(const std::string& message) override;

    // 智能接口:设状态,拿状态/版本
    void setState(const std::string& state);
    std::string getState() const { return state_; }
    int getVersion() const { return version_; }

    // 事件驱动:触发事件
    void triggerEvent(EventType type, const std::string& message);
};
#endif // SMART_SUBJECT_H

智能观察者(双接口兼容)

cpp 复制代码
// smart_observer.h
#ifndef SMART_OBSERVER_H
#define SMART_OBSERVER_H

#include "observer.h"
#include "event_manager.h"
#include <string>
#include <iostream>

// 智能观察者:能接传统通知,也能接事件
class SmartObserver : public Observer, public EventObserver, public std::enable_shared_from_this<SmartObserver> {
private:
    std::string name_;
    int notificationCount_ = 0; // 通知计数

public:
    explicit SmartObserver(const std::string& name);
    ~SmartObserver() = default;

    // 传统通知
    void update(const std::string& message) override;

    // 事件通知
    void onEvent(const EventData& event) override;

    // 查户口
    std::string getName() const { return name_; }
    int getNotificationCount() const { return notificationCount_; }
};
#endif // SMART_OBSERVER_H

高级模式实战演示

cpp 复制代码
#include "smart_subject.h"
#include "smart_observer.h"
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>

int main() {
    std::cout << "=== C++高级观察者模式实战 ===" << std::endl;

    // 老大升级成智能主题
    auto subject = std::make_shared<SmartSubject>();

    // 三个智能小弟
    auto observer1 = std::make_shared<SmartObserver>("Observer1");
    auto observer2 = std::make_shared<SmartObserver>("Observer2");
    auto observer3 = std::make_shared<SmartObserver>("Observer3");

    // 传统方式收小弟1和2
    subject->attach(observer1);
    subject->attach(observer2);

    std::cout << "\n--- 测试传统小弟 ---" << std::endl;
    subject->setState("Active"); // 状态变,传统小弟收通知
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    subject->setState("Processing");

    // 搞个事件管家,让小弟2订阅状态变化,小弟3订阅系统告警
    auto eventManager = std::make_shared<EventManager>();
    eventManager->subscribe(EventType::STATE_CHANGE, observer2);
    eventManager->subscribe(EventType::SYSTEM_ALERT, observer3);

    std::cout << "\n--- 测试事件驱动 ---" << std::endl;
    subject->triggerEvent(EventType::SYSTEM_ALERT, "System maintenance scheduled"); // 发事件,小弟3响应

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // 踢走小弟1
    std::cout << "\n--- 测试踢人 ---" << std::endl;
    subject->detach(observer1);
    subject->setState("Inactive"); // 状态变,只剩小弟2收通知

    // 看看谁收到多少通知
    std::cout << "\n--- 战绩统计 ---" << std::endl;
    std::cout << observer1->getName() << " 收到 " << observer1->getNotificationCount() << " 条" << std::endl;
    std::cout << observer2->getName() << " 收到 " << observer2->getNotificationCount() << " 条" << std::endl;
    std::cout << observer3->getName() << " 收到 " << observer3->getNotificationCount() << " 条" << std::endl;

    return 0;
}

观察者模式的江湖地位:利与弊

独门优势

  1. 解耦大师:主题和观察者各玩各的,内部细节互不干扰。
  2. 广播高手:主题一声吼,所有小弟都能听到,不用管谁是谁。
  3. 实时先锋:状态一变,通知秒到,数据一致性妥妥的。
  4. 灵活多变:运行时想加小弟、踢小弟,随心所欲。
  5. 遵守开闭:加新类型的小弟?主题代码动都不用动。

江湖险恶(缺点)

  1. 性能刺客:小弟太多,挨个通知一圈,时间耗不起。
  2. 循环依赖坑:主题和小弟相互引用没处理好,内存泄漏等着你。
  3. 顺序靠不住:通知顺序?看天(实现)!需要特定顺序得自己控。
  4. 调试迷宫:通知自动触发,出问题时追踪调用链像走迷宫。

啥时候该用它?

这模式最适合这些场子:

  1. 一拖N关系:一个对象状态变,N个对象得跟着变。
  2. 广播大喇叭:需要广而告之,不用知道具体谁在听。
  3. 解耦是王道:不想让消息发送方和接收方绑太死,方便扩展维护。
  4. 动态增减员:系统运行时需要灵活加人或减人。

猫哥结语

观察者模式绝对是对象间通信的利器,用好了能让代码灵活又解耦。在C++里玩转它,关键注意几点:

  1. 内存江湖险恶shared_ptrweak_ptr用好,避免内存泄漏这个江湖大敌。
  2. 多线程要稳 :锁(mutex)是保护小弟列表和通知过程的必备铠甲。
  3. 事件设计要巧:高级玩法里,事件类型和数据结构设计好了,扩展性才强。
  4. 性能瓶颈得防:小弟成千上万?考虑异步通知、批量通知这些优化大招。

通过这篇长文和硬核代码,观察者模式从基础到进阶的门道应该都摸清了。下次在项目里遇到对象间需要高效"通风报信",就大胆用上它吧!

相关推荐
deng-c-f2 小时前
C/C++内置库函数(3):future、promise的用法
c语言·开发语言·c++
deng-c-f3 小时前
C/C++内置库函数(6):C++中类什么时候使用静态变量
开发语言·c++
2301_789015623 小时前
C++:模板进阶
c语言·开发语言·汇编·c++
better_liang3 小时前
每日Java面试场景题知识点之-单例模式
java·单例模式·设计模式·面试·企业级开发
sg_knight3 小时前
什么是设计模式?为什么 Python 也需要设计模式
开发语言·python·设计模式
是小胡嘛3 小时前
仿Muduo高并发服务器之Buffer模块
开发语言·c++·算法
im_AMBER3 小时前
Leetcode 75 数对和 | 存在重复元素 II
c++·笔记·学习·算法·leetcode
ZXF_H3 小时前
C/C++ OpenSSL自适应格式解析证书二进制字节流
c语言·开发语言·c++·openssl
koping_wu3 小时前
【设计模式】设计模式原则、单例模式、工厂模式、模板模式、策略模式
单例模式·设计模式·策略模式