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

观察者模式的核心组成

  1. Subject(主题/被观察者)

    • 维护一个观察者列表

    • 提供添加、删除观察者的接口

    • 提供通知观察者的接口

  2. Observer(观察者)

    • 定义一个更新接口,用于在主题状态改变时接收通知

观察者模式的实现方式

1. 经典实现(使用抽象基类)

#include <iostream>
#include <vector>
#include <algorithm>

// 前向声明
class Observer;

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

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

// 具体主题
class ConcreteSubject : public Subject {
private:
    std::vector<Observer*> observers;
    int state;

public:
    void attach(Observer* observer) override {
        observers.push_back(observer);
    }

    void detach(Observer* observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notify() override {
        for (Observer* observer : observers) {
            observer->update(this);
        }
    }

    int getState() const {
        return state;
    }

    void setState(int newState) {
        state = newState;
        notify(); // 状态改变时自动通知观察者
    }
};

// 具体观察者A
class ConcreteObserverA : public Observer {
public:
    void update(Subject* subject) override {
        if (auto concreteSubject = dynamic_cast<ConcreteSubject*>(subject)) {
            std::cout << "ConcreteObserverA: Subject state changed to " 
                      << concreteSubject->getState() << std::endl;
        }
    }
};

// 具体观察者B
class ConcreteObserverB : public Observer {
public:
    void update(Subject* subject) override {
        if (auto concreteSubject = dynamic_cast<ConcreteSubject*>(subject)) {
            std::cout << "ConcreteObserverB: Subject state changed to " 
                      << concreteSubject->getState() << std::endl;
        }
    }
};

// 使用示例
int main() {
    ConcreteSubject subject;
    ConcreteObserverA observerA;
    ConcreteObserverB observerB;

    subject.attach(&observerA);
    subject.attach(&observerB);

    subject.setState(10); // 两个观察者都会收到通知
    subject.setState(20);

    subject.detach(&observerB);
    subject.setState(30); // 只有observerA会收到通知

    return 0;
}

2. 使用现代C++(智能指针、lambda等)

#include <iostream>
#include <vector>
#include <memory>
#include <functional>

class Subject {
private:
    std::vector<std::function<void(int)>> observers;
    int state;

public:
    void attach(const std::function<void(int)>& observer) {
        observers.push_back(observer);
    }

    void notify() {
        for (const auto& observer : observers) {
            observer(state);
        }
    }

    void setState(int newState) {
        state = newState;
        notify();
    }
};

int main() {
    Subject subject;

    // 使用lambda表达式作为观察者
    auto observer1 = [](int state) {
        std::cout << "Observer1: State changed to " << state << std::endl;
    };

    auto observer2 = [](int state) {
        std::cout << "Observer2: State changed to " << state << std::endl;
    };

    subject.attach(observer1);
    subject.attach(observer2);

    subject.setState(10);
    subject.setState(20);

    return 0;
}

3. 使用信号槽机制(类似Qt的信号槽)

#include <iostream>
#include <vector>
#include <functional>
#include <memory>

template <typename... Args>
class Signal {
private:
    std::vector<std::function<void(Args...)>> slots;

public:
    void connect(const std::function<void(Args...)>& slot) {
        slots.push_back(slot);
    }

    void emit(Args... args) {
        for (auto& slot : slots) {
            slot(args...);
        }
    }
};

class Model {
public:
    Signal<int> dataChanged;

    void setData(int newData) {
        // 数据改变时发射信号
        dataChanged.emit(newData);
    }
};

class View {
public:
    void update(int data) {
        std::cout << "View updated with data: " << data << std::endl;
    }
};

int main() {
    Model model;
    View view;

    // 连接信号和槽
    model.dataChanged.connect([&view](int data) { view.update(data); });

    model.setData(10);
    model.setData(20);

    return 0;
}

观察者模式 UML 类图

+-------------------+          +-------------------+
|     Subject       |          |     Observer      |
+-------------------+          +-------------------+
| -observers: List  |<>------->| +update(): void   |
+-------------------+          +-------------------+
| +attach(Observer) |                  ^
| +detach(Observer) |                  |
| +notify(): void   |          +-------------------+
+-------------------+          |   ConcreteObserver |
        ^                      +-------------------+
        |                      | +update(): void   |
+-------------------+          +-------------------+
|  ConcreteSubject  |
+-------------------+
| -state: int       |
+-------------------+
| +getState(): int  |
| +setState(): void |
+-------------------+

UML 元素解析

1. 核心角色
类/接口 作用
Subject 抽象主题(被观察者),维护观察者列表,提供 attach/detach/notify 接口
ConcreteSubject 具体主题,维护状态(state),状态变化时调用 notify() 通知观察者
Observer 抽象观察者,定义 update() 接口
ConcreteObserver 具体观察者,实现 update() 以响应主题状态变化
2. 关系说明
关系类型 描述
关联关系 Subject 聚合 Observer(用菱形箭头表示,主题持有观察者列表)
泛化关系 ConcreteSubject 继承 SubjectConcreteObserver 继承 Observer
依赖关系 Observer::update() 依赖 Subject 作为参数(虚线箭头,可选绘制)

时序图示例

ConcreteSubject       ConcreteObserver
      |                      |
      | setState(10)         |
      |--------------------->| notify()
      |                      |
      |                      | update()
      |<---------------------|
      |                      |
  1. 主题调用 setState() 修改状态。

  2. 主题内部调用 notify() 遍历观察者列表。

  3. 每个观察者执行 update() 响应变化。

关键设计点

  1. 松耦合

    • 主题无需知道观察者的具体类,只需调用 update() 接口。

    • 新增观察者无需修改主题代码(符合开闭原则)。

  2. 推模型 vs 拉模型

    • 推模型:主题将状态通过 update() 参数传递给观察者(如 update(int state))。

    • 拉模型:观察者从主题主动获取状态(如 update() 内调用 subject.getState())。

  3. 线程安全

    • 多线程环境下需对观察者列表的修改(attach/detach)和遍历(notify)加锁。

代码映射 UML

// Subject(抽象主题)
class Subject {
protected:
    std::vector<Observer*> observers;  // 聚合 Observer
public:
    virtual void attach(Observer* obs) = 0;
    virtual void notify() = 0;
};

// ConcreteSubject(具体主题)
class WeatherData : public Subject {
private:
    int temperature;  // 状态
public:
    void setTemperature(int temp) {
        temperature = temp;
        notify();     // 状态变化时通知观察者
    }
};

// Observer(抽象观察者)
class Observer {
public:
    virtual void update(Subject* sub) = 0;  // 依赖 Subject
};

// ConcreteObserver(具体观察者)
class Display : public Observer {
public:
    void update(Subject* sub) override {
        if (auto weather = dynamic_cast<WeatherData*>(sub)) {
            std::cout << "Temperature: " << weather->getTemperature();
        }
    }
};

常见变体

  1. 事件总线(Event Bus)

    • 主题扩展为全局事件中心,支持多类型事件订阅/发布。

  2. 信号槽(Signal-Slot)

    • 如 Qt 框架,用 connect() 绑定信号发射器和槽函数。

观察者模式的优缺点

优点

  1. 支持简单的广播通信,自动通知所有已订阅的对象

  2. 主题和观察者之间松耦合,主题不需要知道具体的观察者类

  3. 可以在运行时动态添加和删除观察者

  4. 符合开放-封闭原则,可以引入新的观察者而不修改主题

缺点

  1. 通知顺序不可控

  2. 如果观察者处理不当,可能导致循环引用

  3. 观察者过多时,通知可能花费较长时间

  4. 观察者不知道彼此的存在,可能导致更新冲突

观察者模式的适用场景

  1. 当一个对象的状态改变需要通知其他多个对象时

  2. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时

  3. 需要在运行时动态建立和解除对象间关系时

  4. 需要实现类似广播机制的功能时

实际应用示例

  1. GUI事件处理(如按钮点击事件)

  2. 发布-订阅系统

  3. 模型-视图-控制器(MVC)架构中的模型和视图

  4. 股票价格变动通知

  5. 游戏中的事件系统

注意事项

  1. 内存管理:注意避免循环引用,特别是在使用shared_ptr时

  2. 线程安全:在多线程环境下使用时需要确保线程安全

  3. 性能考虑:当观察者数量很大时,通知操作可能成为性能瓶颈

  4. 更新顺序:观察者的更新顺序通常是不确定的,不要依赖特定的顺序

      观察者模式是C++中非常有用且常见的设计模式,通过解耦主题和观察者,非常适合实现事件驱动架构和用户界面开发中。现代C++的特性(如lambda表达式、智能指针等)使得实现观察者模式更加简洁和安全。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐