15.C++设计模式-观察者模式

第一部分:基础概念与实现

1. 概念定义

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者,使它们能够自动更新自己。

2. 核心组成

Subject
-observers: List<Observer>
+attach(Observer)
+detach(Observer)
+notify()
<<interface>>
Observer
+update()
ConcreteSubject
-state: State
+getState()
+setState()
ConcreteObserver
-state: State
+update()

3. 工作原理流程图

Observer2 Observer1 Subject Client Observer2 Observer1 Subject Client attach(Observer1) attach(Observer2) setState(newState) update() getState() return state update() getState() return state

4. 应用场景

  • 事件处理系统:GUI程序中的按钮点击、键盘输入等事件
  • 发布-订阅系统:消息队列、事件总线
  • 数据同步:多个视图需要实时更新同一份数据
  • 游戏开发:角色状态变化通知UI更新
  • MVC架构:Model变化时通知View更新

5. C++完整实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

// 前向声明
class Observer;

// 主题接口(被观察者)
class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify() = 0;
};

// 观察者接口
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(int value) = 0;
};

// 具体主题:天气站
class WeatherStation : public Subject {
private:
    std::vector<Observer*> observers;
    int temperature;

public:
    WeatherStation() : temperature(0) {}
    
    void attach(Observer* observer) override {
        observers.push_back(observer);
        std::cout << "观察者已添加" << 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 << "观察者已移除" << std::endl;
        }
    }
    
    void notify() override {
        std::cout << "通知所有观察者..." << std::endl;
        for (auto* observer : observers) {
            observer->update(temperature);
        }
    }
    
    // 业务方法:设置温度
    void setTemperature(int temp) {
        std::cout << "\n天气站:温度从 " << temperature 
                  << " 度变为 " << temp << " 度" << std::endl;
        temperature = temp;
        notify();  // 状态改变,通知观察者
    }
};

// 具体观察者1:手机App
class PhoneApp : public Observer {
private:
    std::string phoneName;
    
public:
    PhoneApp(const std::string& name) : phoneName(name) {}
    
    void update(int temperature) override {
        std::cout << "📱 " << phoneName << " App 显示:当前温度 " 
                  << temperature << " 度" << std::endl;
    }
};

// 具体观察者2:LED显示屏
class LEDDisplay : public Observer {
private:
    std::string location;
    
public:
    LEDDisplay(const std::string& loc) : location(loc) {}
    
    void update(int temperature) override {
        std::cout << "🖥️  " << location << " LED显示屏:温度 = " 
                  << temperature << "°C" << std::endl;
    }
};

// 具体观察者3:报警系统
class AlertSystem : public Observer {
private:
    int alertThreshold;
    
public:
    AlertSystem(int threshold) : alertThreshold(threshold) {}
    
    void update(int temperature) override {
        if (temperature > alertThreshold) {
            std::cout << "⚠️  报警系统:高温警报!温度 " << temperature 
                      << " 度超过阈值 " << alertThreshold << " 度" << std::endl;
        } else if (temperature < -10) {
            std::cout << "⚠️  报警系统:低温警报!温度 " << temperature 
                      << " 度过低,请注意保暖" << std::endl;
        } else {
            std::cout << "✅ 报警系统:温度正常" << std::endl;
        }
    }
};

// 使用示例
int main() {
    // 创建主题
    WeatherStation weatherStation;
    
    // 创建观察者
    PhoneApp phone1("张三的手机");
    PhoneApp phone2("李四的手机");
    LEDDisplay led("中央广场");
    AlertSystem alert(35);  // 超过35度报警
    
    // 注册观察者
    weatherStation.attach(&phone1);
    weatherStation.attach(&phone2);
    weatherStation.attach(&led);
    weatherStation.attach(&alert);
    
    // 模拟温度变化
    weatherStation.setTemperature(25);
    weatherStation.setTemperature(38);
    weatherStation.setTemperature(-15);
    
    // 移除一个观察者
    weatherStation.detach(&phone2);
    std::cout << "\n=== 移除李四手机后 ===" << std::endl;
    weatherStation.setTemperature(20);
    
    return 0;
}

6. 现代C++改进版本(使用智能指针和函数式)

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
#include <algorithm>

// 使用函数式观察者(更灵活)
class WeatherStationModern {
private:
    std::vector<std::function<void(int)>> callbacks;
    int temperature;
    
public:
    void subscribe(std::function<void(int)> callback) {
        callbacks.push_back(callback);
    }
    
    void setTemperature(int temp) {
        temperature = temp;
        notify();
    }
    
    void notify() {
        for (const auto& callback : callbacks) {
            callback(temperature);
        }
    }
};

int main() {
    WeatherStationModern station;
    
    // 使用lambda表达式作为观察者
    station.subscribe([](int temp) {
        std::cout << "Lambda观察者1:温度是 " << temp << "°C" << std::endl;
    });
    
    station.subscribe([](int temp) {
        if (temp > 30) {
            std::cout << "Lambda观察者2:天气很热!" << std::endl;
        }
    });
    
    station.setTemperature(28);
    station.setTemperature(35);
    
    return 0;
}

7. 传统版本的优缺点

优点:

  • ✅ 满足开闭原则,添加新观察者无需修改主题
  • ✅ 松耦合,主题和观察者可以独立变化
  • ✅ 支持广播通信

缺点:

  • ❌ 观察者过多时通知开销大
  • ❌ 如果观察者之间有依赖,需要注意通知顺序
  • ❌ 可能造成循环依赖或意外更新

8. 注意事项

  1. 避免循环引用 :使用原始指针或weak_ptr防止内存泄漏
  2. 异常安全:确保一个观察者异常不影响其他观察者
  3. 线程安全:多线程环境下需要加锁保护观察者列表
  4. 通知顺序:通常不保证观察者更新顺序

第二部分:思想思维深度解析

1. 核心思想:从"主动轮询"到"被动通知"

观察者模式最根本的思维转变是逆转了控制流
观察者思维
状态改变时主动通知
数据源
客户端
传统轮询思维
不断询问
返回状态
客户端
数据源

传统思维的困境

cpp 复制代码
// 传统轮询方式 - 浪费资源
while(true) {
    if (weatherStation.getTemperature() != lastTemp) {
        updateDisplay();
    }
    sleep(1); // 要么延迟导致响应慢,要么频繁检查浪费CPU
}

观察者思维的优势

cpp 复制代码
// 事件驱动方式 - 精确响应
void onTemperatureChanged(int newTemp) {
    updateDisplay(); // 只在变化时执行
}

2. 哲学层面的思考

"好莱坞原则"(Don't call us, we'll call you)
  • 传统:底层组件主动调用高层组件
  • 观察者:高层组件告诉底层组件"有变化通知我",底层反过来通知高层
"观察"与"被观察"的关系本质

现实世界的映射
编辑部与订阅者
股票交易所与显示屏
传感器与监控中心
发布-订阅模式
观察者模式

3. 思维模型的三个层次

第一层:技术视角(What)
  • 接口分离:Subject和Observer接口
  • 动态注册:运行时添加/移除观察者
  • 广播机制:一对多通知
第二层:架构视角(How)
cpp 复制代码
// 思维模型:事件总线架构
class EventBus {
    // 所有通信都通过这个"中介"
    // 让组件之间完全解耦
};

// vs 直接耦合
class Button {
    Display* display;  // 按钮直接依赖显示器(紧耦合)
    void onClick() {
        display->update();
    }
};
第三层:业务视角(Why)
cpp 复制代码
// 业务需求:电商促销系统
class ProductPrice {
    // 当价格变化时需要通知:
    // 1. 购物车(重新计算总价)
    // 2. 推荐系统(更新推荐)
    // 3. 价格追踪器(记录历史)
    // 4. 邮件通知(订阅降价提醒)
    
    // 使用观察者思维:价格只负责"通知变化"
    // 不关心谁会响应
};

4. 与其他模式的关系思维

思维延伸
变体形式
常与结合
对比理解
Model通知View更新
引入Broker解耦
集中控制通信
函数指针方式
观察者模式
MVC模式
发布-订阅模式
中介者模式
回调机制

5. 常见误区与深化理解

误区1:观察者只是回调函数的包装
cpp 复制代码
// 错误认知:简单回调就够了
void onEvent(std::function<void()> callback);

// 正确理解:观察者模式强调的是"可管理的依赖关系"
class Observable {
    vector<Observer> observers;  // 可管理列表
    // 支持:注册、注销、优先级、条件通知等
};
误区2:观察者必须是被动更新
cpp 复制代码
// 主动拉取 vs 被动推送
class SmartObserver {
    void update(Subject* sub) override {
        // 主动决定要获取什么数据
        int needed = sub->getSpecificData();
        // 而不是被动接受所有数据
    }
};

6. 高级思维:观察者模式的变体

异步观察者
cpp 复制代码
// 思维:通知不应该阻塞主体
class AsyncSubject {
    void notify() {
        for (auto& obs : observers) {
            thread_pool.submit([&obs] {
                obs->update();  // 异步执行
            });
        }
    }
};
条件观察者
cpp 复制代码
// 思维:只有满足条件才通知
class ConditionalSubject {
    void notify(int value) {
        for (auto& obs : observers) {
            if (obs->interest(value)) {  // 观察者声明兴趣
                obs->update(value);
            }
        }
    }
};

7. 现实世界的类比思维

真实世界类比
思维
思维
思维
思维
YouTube频道
观察者模式
微信群消息
观察者模式
股票盯盘
观察者模式
传感器网络
观察者模式

核心思维提取

  1. 关注点分离:发布者只管发布,订阅者只管处理
  2. 动态性:可以随时订阅/取消订阅
  3. 松耦合:双方不知道对方的具体实现
  4. 扩展性:新增订阅者不影响发布者

8. 思维训练:何时不应该使用

cpp 复制代码
// 反模式1:只有一个观察者
class SingletonObserver {
    // 此时观察者模式过度设计
};

// 反模式2:同步紧耦合的需求
class TightCoupled {
    void operation() {
        step1();
        step2();  // 必须立即同步执行
        step3();
    }
    // 观察者模式的异步通知会破坏顺序
};

9. 思维升华:事件驱动架构的基础

观察者模式是事件驱动架构的最小单元:
观察者模式
CQRS模式
事件溯源
事件驱动架构
微服务架构

终极思维:将系统看作"事件流"而非"状态机"

  • 传统思维:当前温度是25度(关注状态)
  • 观察者思维:温度从20变到25度(关注事件)

10. 实践建议

  1. 从简单开始:先使用函数回调,重构到观察者模式
  2. 识别变化点:找出系统中"经常变化"和"需要响应变化"的部分
  3. 命名很重要 :使用业务语言(onPriceChanged而非notify
  4. 文档化通知顺序:明确观察者的执行顺序是否有要求
  5. 考虑线程安全:多线程环境下的观察者列表保护

总结

思维总结 :观察者模式的核心不是技术实现,而是认识到系统中存在独立变化依赖于变化的两类组件,通过建立"通知-响应"的契约,让系统能够灵活演化。

观察者模式是设计模式中最常用的一种,理解它对于构建可扩展、松耦合的系统至关重要。从基础实现到深层思维,这个模式教会我们如何构建真正灵活、可维护的软件系统。

相关推荐
c++之路1 小时前
CMake 系列教程(二):基础命令详解
开发语言·c++
南境十里·墨染春水5 小时前
C++ 工厂模式:从入门到进阶,彻底掌握对象创建的艺术
开发语言·c++·算法
workflower7 小时前
使用大语言模型处理用户需求
大数据·人工智能·设计模式·重构·动态规划
一拳一个呆瓜8 小时前
【STL】_SCL_SECURE_NO_WARNINGS
c++·stl
小小编程路9 小时前
C++ 异常 完整讲解
开发语言·c++
Frank学习路上12 小时前
【C++】面试:关键字与语法特性
c++·面试
geovindu12 小时前
go: Generators Pattern
开发语言·后端·设计模式·golang·生成器模式
Irissgwe13 小时前
数据结构-栈和队列
数据结构·c++·c·栈和队列
点云侠14 小时前
PCL 生成三棱锥点云
c++·算法·最小二乘法
.道阻且长.14 小时前
C++ string 操作指南:接口解析
java·c语言·开发语言·c++