C++ 观察者模式详解

发布于:2025-05-10 ⋅ 阅读:(12) ⋅ 点赞:(0)

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象间的一对多依赖关系,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)都会自动得到通知并更新。

核心概念

设计原则

观察者模式遵循以下设计原则:

  1. 松耦合:主题和观察者之间松耦合

  2. 开闭原则:可以新增观察者而不修改主题

  3. 抽象耦合:主题只依赖观察者抽象接口

主要优点

  1. 动态订阅:观察者可动态订阅或取消订阅

  2. 广播通信:主题可通知多个观察者

  3. 解耦:分离观察者和被观察者

  4. 事件处理:适用于事件驱动系统

模式结构

主要组件

  1. Subject(主题/被观察者)

    • 维护观察者列表

    • 提供注册/注销观察者的接口

    • 状态改变时通知观察者

  2. Observer(观察者接口)

    • 定义更新接口

  3. ConcreteObserver(具体观察者)

    • 实现更新接口

    • 维护对主题的引用(可选)

完整代码示例

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <string>

// ==================== 观察者接口 ====================
class Observer {
public:
    virtual void update(const std::string& message) = 0;
    virtual ~Observer() = default;
};

// ==================== 主题接口 ====================
class Subject {
    std::vector<Observer*> observers_;
    std::string state_;
    
public:
    void attach(Observer* observer) {
        observers_.push_back(observer);
    }
    
    void detach(Observer* observer) {
        observers_.erase(
            std::remove(observers_.begin(), observers_.end(), observer),
            observers_.end());
    }
    
    void notify() {
        for (auto observer : observers_) {
            observer->update(state_);
        }
    }
    
    void setState(const std::string& state) {
        state_ = state;
        notify();
    }
    
    std::string getState() const {
        return state_;
    }
};

// ==================== 具体观察者 ====================
class ConcreteObserver : public Observer {
    std::string name_;
    std::string observerState_;
    Subject* subject_;
    
public:
    ConcreteObserver(const std::string& name, Subject* subject)
        : name_(name), subject_(subject) {
        subject_->attach(this);
    }
    
    ~ConcreteObserver() {
        if (subject_) {
            subject_->detach(this);
        }
    }
    
    void update(const std::string& message) override {
        observerState_ = message;
        std::cout << "观察者 " << name_ << " 收到更新: " << observerState_ << std::endl;
    }
    
    void unsubscribe() {
        if (subject_) {
            subject_->detach(this);
            subject_ = nullptr;
        }
    }
};

// ==================== 客户端代码 ====================
int main() {
    std::cout << "=== 观察者模式演示 ===" << std::endl;
    
    // 创建主题
    Subject subject;
    
    // 创建观察者
    ConcreteObserver observer1("观察者1", &subject);
    ConcreteObserver observer2("观察者2", &subject);
    ConcreteObserver observer3("观察者3", &subject);
    
    // 改变主题状态,观察者会自动收到通知
    std::cout << "\n第一次状态改变:" << std::endl;
    subject.setState("状态1");
    
    // 观察者2取消订阅
    observer2.unsubscribe();
    
    // 再次改变状态
    std::cout << "\n第二次状态改变(观察者2已取消订阅):" << std::endl;
    subject.setState("状态2");
    
    // 动态添加新观察者
    std::cout << "\n添加新观察者:" << std::endl;
    ConcreteObserver observer4("观察者4", &subject);
    subject.setState("状态3");
    
    return 0;
}

模式变体

1. 推模型 vs 拉模型

// 推模型 - 主题将详细数据推送给观察者
class PushObserver {
public:
    virtual void update(int temp, int humidity, float pressure) = 0;
};

// 拉模型 - 观察者从主题拉取所需数据
class PullObserver {
public:
    virtual void update(Subject* subject) = 0; // 观察者自己获取数据
};

2. 使用智能指针管理观察者

class SafeSubject {
    std::vector<std::weak_ptr<Observer>> observers_;
    
    void notify() {
        auto it = observers_.begin();
        while (it != observers_.end()) {
            if (auto observer = it->lock()) {
                observer->update(state_);
                ++it;
            } else {
                it = observers_.erase(it);
            }
        }
    }
};

3. 线程安全的观察者模式

#include <mutex>

class ThreadSafeSubject {
    std::vector<Observer*> observers_;
    mutable std::mutex mtx_;
    
public:
    void attach(Observer* observer) {
        std::lock_guard<std::mutex> lock(mtx_);
        observers_.push_back(observer);
    }
    
    void notify() {
        std::vector<Observer*> observersCopy;
        {
            std::lock_guard<std::mutex> lock(mtx_);
            observersCopy = observers_;
        }
        
        for (auto observer : observersCopy) {
            observer->update(state_);
        }
    }
};

实际应用场景

  1. GUI事件处理:按钮点击、键盘输入等事件监听

  2. 发布-订阅系统:消息队列、新闻推送

  3. 股票行情更新:股价变动通知投资者

  4. 游戏引擎:游戏状态变化通知UI更新

  5. 分布式系统:配置变更通知多个节点


网站公告

今日签到

点亮在社区的每一天
去签到