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. 工作原理流程图

Observer2Observer1SubjectClientObserver2Observer1SubjectClientattach(Observer1)attach(Observer2)setState(newState)update()getState()return stateupdate()getState()return state

4. 应用场景

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

5. C++完整实现

#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++改进版本(使用智能指针和函数式)

#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. 核心思想:从"主动轮询"到"被动通知"

观察者模式最根本的思维转变是逆转了控制流

观察者思维

状态改变时主动通知

数据源

客户端

传统轮询思维

不断询问

返回状态

客户端

数据源

传统思维的困境

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

观察者思维的优势

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

2. 哲学层面的思考

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

现实世界的映射

编辑部与订阅者

股票交易所与显示屏

传感器与监控中心

发布-订阅模式

观察者模式

3. 思维模型的三个层次

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

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

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

思维延伸

变体形式

常与结合

对比理解

Model通知View更新

引入Broker解耦

集中控制通信

函数指针方式

观察者模式

MVC模式

发布-订阅模式

中介者模式

回调机制

5. 常见误区与深化理解

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

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

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

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

7. 现实世界的类比思维

真实世界类比

思维

思维

思维

思维

YouTube频道

观察者模式

微信群消息

观察者模式

股票盯盘

观察者模式

传感器网络

观察者模式

核心思维提取

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

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

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

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

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

观察者模式是事件驱动架构的最小单元:

观察者模式

CQRS模式

事件溯源

事件驱动架构

微服务架构

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

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

10. 实践建议

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

总结

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值