一、观察者模式概述

观察者模式(Observer Pattern)是一种行为型设计模式,用于建立对象间的一对多依赖关系。当一个对象(被观察者)状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并更新。这种模式通过解耦通知方与接收方,实现了系统的灵活性和可扩展性。


二、传统代码 vs 观察者模式对比

1. 传统实现(紧耦合)

// 抽象显示元素接口,强制子类实现 display 方法
class DisplayElement {
public:
    virtual void display() = 0;
};

// 具体显示类:直接绑定 WeatherData 数据
class CurrentConditionsDisplay : public DisplayElement {
public:
    // 接收 WeatherData 的更新数据并刷新显示
    void update(float temp, float humidity, float pressure) {
        this->temp = temp;
        this->humidity = humidity;
        this->pressure = pressure;
        display();  // 调用 display 展示最新数据
    }

    void display() override {
        std::cout << "Current conditions: " << temp << "F degrees and " << humidity << "% humidity" << std::endl;
    }

private:
    float temp, humidity, pressure;
};

// 被观察者类:直接持有显示对象(紧耦合)
class WeatherData {
public:
    // 设置天气数据并直接触发显示更新
    void setMeasurements(float temp, float humidity, float pressure) {
        this->temp = temp;
        this->humidity = humidity;
        this->pressure = pressure;
        
        // ❗️直接调用具体观察者方法,形成硬编码依赖
        currentDisplay.update(temp, humidity, pressure);
    }

private:
    CurrentConditionsDisplay currentDisplay;  // ❗️硬编码依赖具体观察者
    float temp, humidity, pressure;
};

问题分析:

  • 紧耦合问题WeatherData 需要直接包含具体观察者类(如 CurrentConditionsDisplay
  • 扩展困难:添加新观察者需要修改 WeatherData
  • 违反开闭原则:系统难以对扩展开放,对修改关闭

2. 观察者模式实现(松耦合)

// 抽象观察者接口:定义统一的更新协议
class IObserver {
public:
    // 纯虚函数:子类必须实现 update 方法
    virtual void update(float temp, float humidity, float pressure) = 0;
    virtual ~IObserver() = default;  // 虚析构确保多态释放
};

// 抽象被观察者接口:定义观察者管理操作
class ISubject {
public:
    // 注册观察者
    virtual void registerObserver(IObserver* observer) = 0;
    // 移除观察者
    virtual void removeObserver(IObserver* observer) = 0;
    // 通知所有观察者
    virtual void notifyObservers() = 0;
};

// 具体被观察者实现
class WeatherData : public ISubject {
public:
    // 实现注册观察者方法
    void registerObserver(IObserver* observer) override {
        observers.push_back(observer);  // 将观察者加入列表
    }

    // 实现移除观察者方法
    void removeObserver(IObserver* observer) override {
        // 使用 std::remove 重排容器,然后 erase 删除
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    // 实现通知观察者方法
    void notifyObservers() override {
        for (auto& observer : observers) {
            observer->update(temp, humidity, pressure);  // 通过接口调用更新
        }
    }

    // 设置天气数据并触发通知
    void setMeasurements(float temp, float humidity, float pressure) {
        this->temp = temp;
        this->humidity = humidity;
        this->pressure = pressure;
        measurementsChanged();  // 触发状态变化通知
    }

private:
    // 状态变化时调用通知逻辑
    void measurementsChanged() {
        notifyObservers();  // 通知所有注册观察者
    }
    
    std::vector<IObserver*> observers;  // 存储观察者指针列表
    float temp, humidity, pressure;
};

// 具体观察者实现
class CurrentConditionsDisplay : public IObserver {
public:
    // 构造函数自动注册到 WeatherData
    CurrentConditionsDisplay(WeatherData& weatherData) {
        this->weatherData.registerObserver(this);  // 将自身注册为观察者
    }

    // 实现 update 方法
    void update(float temp, float humidity, float pressure) override {
        this->temp = temp;
        this->humidity = humidity;
        this->pressure = pressure;
        display();  // 刷新显示
    }

    void display() {
        std::cout << "Current conditions: " << temp << "F degrees and " << humidity << "% humidity" << std::endl;
    }

private:
    float temp, humidity, pressure;
    WeatherData& weatherData;  // 引用 WeatherData 实例
};

(一堆观察者,一堆被观察者,一堆接口;被观察者提供观察者注册、解绑、通知接口,被观察者提供数据更新并通知方法;观察者实现被观察者注册构造方法,实现更新接口,实现展示方法)

改进优势:

  • 松耦合WeatherData 只依赖抽象接口 IObserver
  • 可扩展性强:新增观察者只需实现 IObserver 接口
  • 符合开闭原则:无需修改 WeatherData 即可扩展功能

三、Mermaid 类图说明

1
n
uses
ISubject
+registerObserver(observer: IObserver) : void
+removeObserver(observer: IObserver) : void
+notifyObservers() : void
IObserver
+update(temp: float, humidity: float, pressure: float) : void
WeatherData
-observers: std::vector
+setMeasurements(temp: float, humidity: float, pressure: float) : void
CurrentConditionsDisplay
+display() : void

四、核心设计要点

1. 接口分层设计

  • ISubject 接口:定义观察者管理方法(注册/移除/通知)
  • IObserver 接口:定义更新方法的标准化协议
  • 具体实现类:通过组合方式实现接口协作

2. 通知机制实现

void WeatherData::notifyObservers() {
    for (auto& observer : observers) {
        observer->update(temp, humidity, pressure);  // 通过接口调用具体实现
    }
}
  • 使用迭代器遍历观察者列表
  • 通过统一接口调用更新方法
  • 实现"广播-订阅"通信模式

3. 扩展性验证

新增统计显示观察者只需:

class StatisticsDisplay : public IObserver {
public:
    void update(float temp, float humidity, float pressure) override {
        temps.push_back(temp);
        // 计算统计信息...
        display();
    }
    void display() {
        std::cout << "Stats: Max/Min/Avg temperature..." << std::endl;
    }
private:
    std::vector<float> temps;
};

无需修改 WeatherData 类即可实现功能扩展


五、应用场景与注意事项

适用场景

  • GUI 事件处理系统(如按钮点击事件)
  • 实时数据监控系统
  • 分布式事件总线架构
  • 游戏中的事件驱动系统

注意事项

1. 内存管理:需注意观察者生命周期管理,避免野指针

2. 通知顺序:观察者列表顺序可能影响业务逻辑

3. 线程安全:多线程环境下需考虑同步机制

4. 异常处理:单个观察者异常不应影响整体通知流程


六、现代C++改进方案

使用智能指针和lambda表达式增强安全性:

class ModernWeatherData : public ISubject {
public:
    void registerObserver(std::shared_ptr<IObserver> observer) override {
        observers.push_back(observer);  // 使用智能指针避免内存泄漏
    }
    
    // ...其他方法...
private:
    std::vector<std::shared_ptr<IObserver>> observers;  // 智能指针容器
};

七、总结

特性 传统实现 观察者模式
对象耦合度
扩展性 需修改现有代码 支持开闭原则
维护成本
通知灵活性 固定调用 动态注册/注销
适用场景复杂度 简单场景 复杂系统架构

观察者模式通过接口抽象和行为封装,为复杂系统提供了优雅的通信解决方案。在C++中实现时需注意接口设计规范、内存管理和线程安全等工程实践问题,合理使用智能指针和现代C++特性可进一步提升代码质量。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐