观察者模式(Observer Pattern)

在 C++ 中,观察者模式(Observer Pattern) 是一种行为设计模式,用于建立对象之间的一对多依赖关系。当一个对象的状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并更新自己的状态。这种模式非常适合解耦发布者和订阅者之间的关系,常用于事件处理、UI 更新、日志系统等场景。


观察者模式用于什么?

  1. 事件通知
    • 当某个事件发生时(例如用户点击按钮),需要通知多个对象执行相应操作。
  2. 状态同步
    • 一个对象的状态改变时,多个依赖对象需要同步更新(例如数据模型变化时更新视图)。
  3. 解耦系统
    • 让发布者(Subject)和订阅者(Observer)独立运行,降低耦合度,方便扩展。
  4. 典型场景
    • GUI 框架(如 Qt 的信号与槽机制)。
    • 游戏中角色状态变化通知 UI。
    • 分布式系统中的消息订阅。

观察者模式的组成

  1. Subject(主题/被观察者)
    • 维护一个观察者列表,提供添加、删除观察者的方法,并在状态变化时通知所有观察者。
  2. Observer(观察者)
    • 定义一个更新接口,Subject 状态变化时会调用该接口。
  3. ConcreteSubject
    • Subject 的具体实现,存储状态并在变化时触发通知。
  4. ConcreteObserver
    • Observer 的具体实现,定义收到通知后的具体行为。

C++ 中的实现

下面是一个简单的 C++ 观察者模式实现,包含基本的添加、移除和通知功能。

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

// 前向声明
class Subject;

// 观察者基类(抽象接口)
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const std::string& message) = 0;  // 纯虚函数,接收通知
};

// 主题类(被观察者)
class Subject {
private:
    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 (Observer* observer : observers) {
            observer->update(state);
        }
    }

    void setState(const std::string& newState) {  // 修改状态并通知
        state = newState;
        notify();
    }

    std::string getState() const {
        return state;
    }
};

// 具体观察者 A
class ConcreteObserverA : public Observer {
private:
    std::string name;

public:
    ConcreteObserverA(const std::string& n) : name(n) {}
    void update(const std::string& message) override {
        std::cout << name << " received update: " << message << "\n";
    }
};

// 具体观察者 B
class ConcreteObserverB : public Observer {
private:
    std::string name;

public:
    ConcreteObserverB(const std::string& n) : name(n) {}
    void update(const std::string& message) override {
        std::cout << name << " processed message: " << message << "\n";
    }
};

// 测试代码
int main() {
    Subject subject;

    ConcreteObserverA observerA("Observer A");
    ConcreteObserverB observerB("Observer B");

    // 注册观察者
    subject.attach(&observerA);
    subject.attach(&observerB);

    // 修改状态,触发通知
    subject.setState("State changed to 1");
    std::cout << "----\n";

    // 移除一个观察者
    subject.detach(&observerA);

    // 再次修改状态
    subject.setState("State changed to 2");

    return 0;
}

输出

Observer A received update: State changed to 1
Observer B processed message: State changed to 1
----
Observer B processed message: State changed to 2

实现要点解析

  1. 观察者接口(Observer)

    • 定义纯虚函数 update(),具体观察者需要实现它。
    • 使用虚析构函数确保派生类对象正确销毁。
  2. 主题(Subject)

    • 使用 std::vector<Observer*> 存储观察者指针,动态管理订阅关系。
    • attach()detach() 分别添加和移除观察者。
    • notify() 遍历列表,调用每个观察者的 update()
  3. 状态管理

    • setState() 修改主题状态并触发 notify(),实现"状态变化 -> 通知"的逻辑。
  4. 具体观察者

    • ConcreteObserverAConcreteObserverB 是不同类型的观察者,展示了如何自定义响应行为。

改进建议

上述实现是一个基础版本,实际应用中可以根据需求优化:

  1. 线程安全

    • 如果在多线程环境下使用,需要在 attach()detach()notify() 中加锁(例如使用 std::mutex)。
    cpp 复制代码
    std::mutex mtx;
    void attach(Observer* observer) {
        std::lock_guard<std::mutex> lock(mtx);
        observers.push_back(observer);
    }
  2. 智能指针

    • std::shared_ptr<Observer> 替代裸指针,避免手动管理内存。
    cpp 复制代码
    std::vector<std::shared_ptr<Observer>> observers;
    void attach(std::shared_ptr<Observer> observer) {
        observers.push_back(observer);
    }
  3. 传递更多信息

    • update() 可以携带更多参数(如事件类型、数据指针),而不是简单的一个字符串。
  4. 弱引用

    • 使用 std::weak_ptr 避免观察者被意外销毁后仍留在列表中的问题。

实际应用示例

假设你实现一个温度传感器系统:

  • Subject:温度传感器,记录温度变化。
  • Observers:显示屏(显示温度)、警报器(温度过高时报警)。
cpp 复制代码
class TemperatureSensor {
private:
    std::vector<Observer*> observers;
    double temperature;

public:
    void attach(Observer* o) { observers.push_back(o); }
    void notify() {
        for (auto* o : observers) o->update(std::to_string(temperature));
    }
    void setTemperature(double temp) {
        temperature = temp;
        notify();
    }
};

class Display : public Observer {
public:
    void update(const std::string& message) override {
        std::cout << "Display: Temperature is " << message << "°C\n";
    }
};

class Alarm : public Observer {
public:
    void update(const std::string& message) override {
        double temp = std::stod(message);
        if (temp > 30.0) {
            std::cout << "Alarm: Temperature too high! " << temp << "°C\n";
        }
    }
};

int main() {
    TemperatureSensor sensor;
    Display display;
    Alarm alarm;

    sensor.attach(&display);
    sensor.attach(&alarm);

    sensor.setTemperature(25.0);
    sensor.setTemperature(35.0);

    return 0;
}

输出

Display: Temperature is 25°C
Display: Temperature is 35°C
Alarm: Temperature too high! 35°C

总结

  • 用途:观察者模式用于在对象状态变化时通知多个依赖对象,广泛应用于事件驱动的系统。
  • 实现:通过 Subject 管理观察者列表,Observer 定义更新接口,结合动态注册和通知实现松耦合。
  • 优势:灵活、可扩展,Subject 和 Observer 可以独立开发。
  • 注意:多线程环境下需加锁,内存管理可优化。
相关推荐
seven97_top43 分钟前
【设计模式】从事件驱动到即时更新:掌握观察者模式的核心技巧
java·观察者模式·设计模式
JuicyActiveGilbert1 小时前
【C++设计模式】第二十三篇:观察者模式(Observer)
c++·观察者模式·设计模式
大G哥1 小时前
Java 设计模式:观察者模式
java·开发语言·观察者模式·设计模式
找了一圈尾巴9 小时前
设计模式-观察者模式、状态模式
观察者模式·设计模式·状态模式
赤水无泪21 小时前
行为模式---观察者模式
观察者模式
智想天开1 天前
23. 观察者模式
观察者模式
lina_mua2 天前
前端开发中的设计模式:观察者模式的应用与实践
观察者模式·设计模式
yuanpan5 天前
23种设计模式之《观察者模式(Observer)》在c#中的应用及理解
观察者模式·设计模式·c#
攻城狮7号6 天前
【第16节】C++设计模式(行为模式)-Observer(观察者)模式
c++·观察者模式·设计模式