【C++】简述Observer观察者设计模式附样例(C++实现)

Observer模式是行为型设计模式的核心之一,核心逻辑是一对多依赖:当被观察者(Subject)的状态变化时,所有注册的观察者(Observer)会被自动通知并执行更新操作。

用生活场景类比:

  • 被观察者(Subject):电商平台的商品库存
  • 观察者(Observer):库存预警系统、用户订阅提醒、商家后台
  • 逻辑:商品库存低于阈值(状态变化),所有关联的观察者都会收到通知并执行对应操作。

核心角色

C++中通过抽象类(纯虚函数) 定义接口,具体类实现接口,核心角色分为4个:

  1. Observer(抽象观察者) :纯虚类,定义update更新接口。
  2. ConcreteObserver(具体观察者) :实现update,处理具体的更新逻辑。
  3. Subject(抽象被观察者) :纯虚类,定义attach(添加观察者)、detach(移除观察者)、notify(通知观察者)接口。
  4. ConcreteSubject(具体被观察者) :实现Subject接口,维护观察者列表和自身状态,状态变化时触发notify

C++代码实现

以「商品库存监控」为例,代码包含完整的类设计、内存管理(C++核心)和测试逻辑:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>  // 用于find/erase

// ---------------------- 抽象层 ----------------------
// 前向声明:解决循环依赖问题(Observer需要知道Subject,反之亦然)
class Subject;

// 抽象观察者
class Observer {
public:
    // 纯虚更新接口:接收被观察者的状态(库存)
    virtual void update(int stock) = 0;
    // 虚析构:确保子类析构被正确调用(C++内存管理关键)
    virtual ~Observer() = default;
};

// 抽象被观察者
class Subject {
public:
    // 添加观察者
    virtual void attach(Observer* observer) = 0;
    // 移除观察者
    virtual void detach(Observer* observer) = 0;
    // 通知所有观察者
    virtual void notify() = 0;
    // 虚析构
    virtual ~Subject() = default;
};

// ---------------------- 实现层 ----------------------
// 具体被观察者:商品库存管理
class ProductStock : public Subject {
private:
    std::vector<Observer*> observers_;  // 观察者列表
    int stock_;                         // 核心状态:库存数量
public:
    ProductStock(int init_stock) : stock_(init_stock) {}

    // 添加观察者:避免重复添加
    void attach(Observer* observer) override {
        if (std::find(observers_.begin(), observers_.end(), observer) == observers_.end()) {
            observers_.push_back(observer);
            std::cout << "库存系统:添加观察者 " << typeid(*observer).name() << std::endl;
        }
    }

    // 移除观察者
    void detach(Observer* observer) override {
        auto it = std::find(observers_.begin(), observers_.end(), observer);
        if (it != observers_.end()) {
            observers_.erase(it);
            std::cout << "库存系统:移除观察者 " << typeid(*observer).name() << std::endl;
        }
    }

    // 通知所有观察者:遍历列表调用update
    void notify() override {
        std::cout << "\n库存系统:库存变化,通知所有观察者!" << std::endl;
        for (auto observer : observers_) {
            observer->update(stock_);
        }
    }

    // 更新库存:状态变化时触发通知
    void setStock(int new_stock) {
        if (new_stock != stock_) {  // 仅状态变化时通知
            stock_ = new_stock;
            notify();
        }
    }

    // 获取当前库存(拉模式可选)
    int getStock() const { return stock_; }
};

// 具体观察者1:库存预警系统
class StockAlarm : public Observer {
private:
    int alarm_threshold_;  // 预警阈值
public:
    StockAlarm(int threshold) : alarm_threshold_(threshold) {}

    void update(int stock) override {
        std::cout << "库存预警系统:当前库存 " << stock << " 件";
        if (stock < alarm_threshold_) {
            std::cout << " → 低于预警阈值" << alarm_threshold_ << ",触发预警!";
        }
        std::cout << std::endl;
    }
};

// 具体观察者2:商家后台
class MerchantBackend : public Observer {
public:
    void update(int stock) override {
        std::cout << "商家后台:库存更新为 " << stock << " 件,已同步至系统" << std::endl;
    }
};

// ---------------------- 测试主函数 ----------------------
int main() {
    // 1. 创建被观察者:初始库存100件的商品
    ProductStock product(100);

    // 2. 创建观察者:预警阈值50件的预警系统、商家后台
    StockAlarm alarm(50);
    MerchantBackend backend;

    // 3. 注册观察者
    product.attach(&alarm);
    product.attach(&backend);

    // 4. 更新库存(触发通知):库存降至60件
    std::cout << "\n=== 第一次库存更新:60件 ===" << std::endl;
    product.setStock(60);

    // 5. 再次更新库存(触发通知):库存降至40件(低于预警阈值)
    std::cout << "\n=== 第二次库存更新:40件 ===" << std::endl;
    product.setStock(40);

    // 6. 移除商家后台观察者,再次更新库存
    std::cout << "\n=== 移除商家后台后,库存更新为30件 ===" << std::endl;
    product.detach(&backend);
    product.setStock(30);

    return 0;
}
编译与运行
bash 复制代码
# 编译(g++)
g++ observer.cpp -o observer -std=c++11
# 运行
./observer
输出结果
复制代码
库存系统:添加观察者 9StockAlarm
库存系统:添加观察者 15MerchantBackend

=== 第一次库存更新:60件 ===

库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 60 件
商家后台:库存更新为 60 件,已同步至系统

=== 第二次库存更新:40件 ===

库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 40 件 → 低于预警阈值50,触发预警!
商家后台:库存更新为 40 件,已同步至系统

=== 移除商家后台后,库存更新为30件 ===
库存系统:移除观察者 15MerchantBackend

库存系统:库存变化,通知所有观察者!
库存预警系统:当前库存 30 件 → 低于预警阈值50,触发预警!

C++实现细节

  1. 前向声明class Subject; 解决ObserverSubject的循环依赖问题(观察者需要知道被观察者类型,反之亦然)。
  2. 虚析构函数virtual ~Observer() = default; 是C++的核心规范------确保通过基类指针/引用析构子类对象时,子类析构函数被正确调用,避免内存泄漏。
  3. 观察者列表管理 :使用std::vector存储观察者指针,通过std::find避免重复添加,erase安全移除。
  4. 状态判断优化setStock中判断new_stock != stock_,仅状态真的变化时才触发通知,避免无效调用。
  5. 类型识别typeid(*observer).name() 用于打印观察者类型(不同编译器输出格式可能略有差异,如9StockAlarm表示StockAlarm类)。
推模式 和 拉模式
  • 示例中是推模式 :被观察者主动把stock参数推给观察者(update(int stock))。

  • 拉模式(更灵活):观察者主动从被观察者获取状态,只需修改update接口:

    cpp 复制代码
    // 抽象观察者修改
    virtual void update(Subject* subject) = 0;
    // 具体观察者实现
    void update(Subject* subject) override {
        ProductStock* stock_subject = dynamic_cast<ProductStock*>(subject);
        if (stock_subject) {
            int stock = stock_subject->getStock();
            // 业务逻辑...
        }
    }
    // 被观察者通知时传this
    void notify() override {
        for (auto observer : observers_) {
            observer->update(this);
        }
    }

Observer模式的实际应用

  1. Qt框架QObject::connect 实现的信号与槽机制(核心是观察者模式)。
  2. GUI事件处理:按钮点击、窗口关闭等事件监听(按钮是Subject,回调函数是Observer)。
  3. 日志系统:多个日志输出端(控制台、文件、网络)监听日志事件,自动输出。

总结

  1. C++实现观察者模式的核心是抽象类定义接口+具体类实现,必须注意虚析构和内存管理。
  2. 关键优势:被观察者与观察者松耦合(仅依赖抽象接口),新增观察者无需修改被观察者代码(符合开闭原则)。
  3. 核心逻辑:被观察者维护观察者列表,状态变化时遍历列表调用update,实现自动通知。
相关推荐
小小unicorn2 小时前
[微服务即时通讯系统]3.服务端-环境搭建
数据库·c++·redis·微服务·云原生·架构
耶叶2 小时前
kotlin的修饰符
android·开发语言·kotlin
Vic101012 小时前
java的分布式协议
java·开发语言·分布式
格林威2 小时前
工业相机图像高速存储(C#版):先存内存,后批量转存方法,附堡盟 (Baumer) 相机实战代码!
开发语言·人工智能·数码相机·opencv·计算机视觉·c#·halcon
格林威2 小时前
工业相机图像高速存储(C++版):先存内存,后批量转存方法,附堡盟相机实战代码!
开发语言·c++·人工智能·数码相机·计算机视觉·视觉检测·堡盟相机
所谓伊人,在水一方3332 小时前
【Python数据科学实战之路】第6章 | 高级数据可视化:从统计洞察到交互叙事
开发语言·python·信息可视化
郝学胜-神的一滴2 小时前
力扣86题分隔链表:双链表拆解合并法详解
开发语言·数据结构·算法·leetcode·链表·职场和发展
愿天堂没有C++2 小时前
Pimpl 设计模式(指针指向实现)
开发语言·c++·设计模式
Nuopiane2 小时前
MyPal3(4)
java·开发语言