观察者模式讲解

观察者模式是行为型设计模式 ,也叫发布 - 订阅模式(Publish-Subscribe)

核心一句话总结:建立「一对多」的依赖关系,一个对象(被观察者 / 主题)的状态发生变化时,所有依赖它的对象(观察者)都会收到自动通知并更新。


一、它到底解决什么问题?

不使用观察者模式的痛点

假设你做一个天气监测系统

  • 天气温度变了 → 要手动通知手机、电视、平板、车载设备
  • 代码里要写一堆 phone.update()tv.update()pad.update()
  • 新增一个设备(手表),就要改原有代码 → 严重耦合、违反开闭原则
  • 状态同步混乱,容易漏通知、错通知

使用观察者模式的效果

  • 天气(被观察者)只管自己更新状态
  • 所有设备(观察者)主动订阅天气
  • 天气一变 → 自动广播通知所有订阅者
  • 新增设备只需要加一个观察者,不用改原有代码

二、观察者模式标准结构(4 大核心角色)

观察者模式固定分为4 个角色,缺一不可:

角色 英文名 作用
抽象主题(抽象被观察者) Subject 定义注册、移除、通知观察者的接口,维护观察者列表
具体主题(具体被观察者) ConcreteSubject 实现抽象主题,存储自身状态,状态变化时调用notify()通知所有观察者
抽象观察者 Observer 定义接收通知、更新数据 的接口(update()
具体观察者 ConcreteObserver 实现抽象观察者,收到通知后执行业务逻辑(更新、展示、处理)

三、核心工作流程(极简步骤)

  1. 观察者向被观察者注册(订阅)
  2. 被观察者保存所有注册的观察者到列表
  3. 被观察者状态发生变化
  4. 被观察者调用notify()遍历列表通知所有观察者
  5. 观察者执行update(),完成自身更新

五、C++ 完整代码实现(逐行解析)

气象站通知多个设备为案例,代码可直接运行、注释详细

复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// ===================== 前置声明:观察者类 =====================
class Observer;

// ==============================================
// 1. 抽象主题(抽象被观察者):核心接口定义
// ==============================================
class Subject {
protected:
    // 观察者列表:存储所有订阅的观察者
    vector<Observer*> observerList;

public:
    // 注册观察者(订阅)
    virtual void attach(Observer* observer) = 0;
    // 移除观察者(取消订阅)
    virtual void detach(Observer* observer) = 0;
    // 通知所有观察者(核心方法)
    virtual void notify() = 0;

    virtual ~Subject() = default;
};

// ==============================================
// 2. 抽象观察者:接收通知的接口
// ==============================================
class Observer {
public:
    // 收到通知后执行更新:参数是被观察者传递的新状态
    virtual void update(const string& weatherInfo) = 0;

    virtual ~Observer() = default;
};

// ==============================================
// 3. 具体主题:气象站(真正的被观察者)
// ==============================================
class WeatherStation : public Subject {
private:
    // 被观察者的核心状态:天气信息
    string weather;

public:
    // 实现注册观察者
    void attach(Observer* observer) override {
        observerList.push_back(observer);
        cout << "一个设备已订阅气象站" << endl;
    }

    // 实现移除观察者
    void detach(Observer* observer) override {
        for (auto it = observerList.begin(); it != observerList.end(); it++) {
            if (*it == observer) {
                observerList.erase(it);
                cout << "一个设备已取消订阅" << endl;
                break;
            }
        }
    }

    // 核心:通知所有观察者
    void notify() override {
        cout << "\n===== 气象站天气更新,开始通知所有设备 =====" << endl;
        for (Observer* obs : observerList) {
            // 调用观察者的update方法,传递最新天气
            obs->update(weather);
        }
    }

    // ============= 业务方法:设置天气(状态改变)=============
    void setWeather(const string& newWeather) {
        this->weather = newWeather;
        cout << "\n气象站更新天气:" << newWeather << endl;
        // 状态改变,自动通知
        notify();
    }
};

// ==============================================
// 4. 具体观察者:手机设备
// ==============================================
class PhoneObserver : public Observer {
public:
    void update(const string& weatherInfo) override {
        cout << "[手机设备] 收到最新天气:" << weatherInfo << endl;
    }
};

// ==============================================
// 5. 具体观察者:电视设备
// ==============================================
class TVObserver : public Observer {
public:
    void update(const string& weatherInfo) override {
        cout << "[电视设备] 收到最新天气:" << weatherInfo << endl;
    }
};

// ==============================================
// 6. 具体观察者:智能手表设备
// ==============================================
class WatchObserver : public Observer {
public:
    void update(const string& weatherInfo) override {
        cout << "[智能手表] 收到最新天气:" << weatherInfo << endl;
    }
};

// ==============================================
// 客户端测试代码
// ==============================================
int main() {
    // 1. 创建被观察者:气象站
    WeatherStation* station = new WeatherStation();

    // 2. 创建观察者:多个设备
    Observer* phone = new PhoneObserver();
    Observer* tv = new TVObserver();
    Observer* watch = new WatchObserver();

    // 3. 设备订阅气象站(注册观察者)
    station->attach(phone);
    station->attach(tv);
    station->attach(watch);

    // 4. 气象站改变天气(状态变化 → 自动通知)
    station->setWeather("晴天,25℃,微风");

    // 5. 手表取消订阅
    station->detach(watch);

    // 6. 气象站再次更新天气
    station->setWeather("小雨,18℃,降温");

    // 释放内存
    delete watch;
    delete tv;
    delete phone;
    delete station;

    return 0;
}

运行结果

plaintext

复制代码
一个设备已订阅气象站
一个设备已订阅气象站
一个设备已订阅气象站

气象站更新天气:晴天,25℃,微风

===== 气象站天气更新,开始通知所有设备 =====
[手机设备] 收到最新天气:晴天,25℃,微风
[电视设备] 收到最新天气:晴天,25℃,微风
[智能手表] 收到最新天气:晴天,25℃,微风

一个设备已取消订阅

气象站更新天气:小雨,18℃,降温

===== 气象站天气更新,开始通知所有设备 =====
[手机设备] 收到最新天气:小雨,18℃,降温
[电视设备] 收到最新天气:小雨,18℃,降温

六、观察者模式优缺点

优点

  1. 彻底解耦 被观察者只依赖抽象观察者,观察者只依赖抽象主题,双方互不关心具体实现
  2. 符合开闭原则 新增观察者(设备)无需修改被观察者代码,直接实现 Observer 接口即可
  3. 广播式通知一次状态改变,自动通知所有订阅者,不用手动挨个调用
  4. 动态订阅 / 取消运行时可随时注册、移除观察者,灵活性极高

缺点

  1. 观察者过多时,通知效率低同步通知会阻塞,需异步优化
  2. 可能出现循环依赖观察者通知被观察者,被观察者又通知观察者 → 死循环
  3. 通知顺序不可控默认按注册顺序通知,无法指定优先级
  4. 调试难度增加状态变化链式传递,问题追踪复杂

八、观察者模式 vs 发布 - 订阅模式(易混点)

日常开发中常混用,但严格区分

  1. 观察者模式 :只有被观察者 ↔ 观察者,直接通信,耦合度稍高
  2. 发布 - 订阅模式 :多一个中间代理(Broker / 事件总线) ,发布者和订阅者完全零耦合

比如:

  • RabbitMQ 是发布 - 订阅(有 Broker)
  • 原生观察者模式是直接通知(无中间层)

九、终极总结

  1. 核心:一对多依赖,状态自动广播通知
  2. 角色:抽象主题 + 具体主题 + 抽象观察者 + 具体观察者
  3. 流程:订阅 → 状态变 → 通知 → 更新
  4. 核心价值:解耦、开闭原则、动态扩展
  5. 一句话记忆你关注我,我变了就告诉你,不用我挨个找你。
相关推荐
yaaakaaang4 小时前
十九、观察者模式
java·观察者模式
UrSpecial6 天前
设计模式:观察者模式
观察者模式·设计模式
sg_knight15 天前
设计模式实战:观察者模式(Observer)
python·观察者模式·设计模式
大数据新鸟18 天前
设计模式详解——观察者模式
观察者模式·设计模式
无籽西瓜a20 天前
【西瓜带你学设计模式 | 第二期-观察者模式】观察者模式——推模型与拉模型实现、优缺点与适用场景
java·后端·观察者模式·设计模式
君主黑暗21 天前
设计模式-观察者模式
观察者模式·设计模式
砍光二叉树21 天前
【设计模式】行为型-观察者模式
java·观察者模式·设计模式
Aaron_dw1 个月前
QT软件开发设计模式-观察者模式
qt·观察者模式·设计模式