C++编程观察者设计模式

观察者设计模式详解

观察者设计模式是一种行为设计模式,用于定义对象间的一对多依赖关系。当一个对象(主题)的状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并更新。


1. 功能和作用
  • 功能:实现对象间的松耦合通信。主题对象维护一个观察者列表,当状态改变时自动通知所有注册的观察者,触发其更新操作。
  • 作用
    • 解耦:主题和观察者不直接依赖具体实现,只通过接口交互。
    • 动态管理:支持运行时添加或删除观察者。
    • 自动更新:确保依赖对象的状态一致性,无需手动轮询。
2. 原理

观察者模式基于发布-订阅机制:

  • 主题(Subject)维护一个观察者集合。
  • 观察者(Observer)注册到主题上。
  • 当主题状态改变时,调用 notify 方法遍历观察者列表,依次调用每个观察者的 update 方法。
  • 观察者通过 update 方法获取主题的最新状态并执行相应操作。
3. 使用方式

使用观察者模式需定义以下组件:

  • Subject 接口 :声明 attach, detach, notify 方法,用于管理观察者。
  • Observer 接口 :声明 update 方法,用于响应主题通知。
  • ConcreteSubject 类 :实现 Subject 接口,管理状态和观察者列表;状态改变时调用 notify
  • ConcreteObserver 类 :实现 Observer 接口,在 update 方法中定义具体响应逻辑。

使用步骤:

  1. 创建主题和观察者对象。
  2. 观察者通过 attach 方法注册到主题。
  3. 主题状态改变时自动通知所有观察者。
  4. 观察者通过 detach 方法取消注册。
4. 为什么这么设计
  • 降低耦合:主题和观察者通过接口交互,互不知晓内部实现,符合依赖倒置原则。
  • 动态扩展:可随时添加新观察者,无需修改主题代码,支持开闭原则。
  • 高效通知:避免观察者轮询主题状态,减少资源消耗。
  • 事件驱动:适用于异步系统,如 GUI 事件处理或实时数据更新。
5. 使用场景

观察者模式适用于以下场景:

  • 事件处理系统:如按钮点击事件通知多个监听器。
  • 数据监控:如传感器数据变化时通知多个显示模块。
  • 发布-订阅模型:如消息队列中生产者通知多个消费者。
  • 用户界面更新:如模型数据改变时更新多个视图组件。
  • 游戏开发:如角色状态变化时通知 AI 系统。

6. 类图描述

观察者模式的类图包含以下类和方法:

  • Subject
    • 方法:attach(Observer*), detach(Observer*), notify()
    • 属性:observers(观察者列表)
  • ConcreteSubject
    • 继承自 Subject
    • 方法:getState(), setState()(设置状态并触发通知)
    • 属性:state(具体状态)
  • Observer
    • 方法:update()
  • ConcreteObserver
    • 继承自 Observer
    • 方法:update()(实现具体响应逻辑)
    • 属性:subject(关联的主题)

类图关系:

  • Subject 聚合 Observer(一对多)。
  • ConcreteObserver 依赖于 ConcreteSubject。

如下类图所示:


7. 时序图描述

对象工作流程时序图描述主题状态变化时的通知过程:

  1. 注册阶段
    • ConcreteObserver 调用 ConcreteSubject 的 attach 方法注册。
  2. 状态改变阶段
    • 客户端调用 ConcreteSubject 的 setState 方法改变状态。
    • ConcreteSubject 内部调用 notify
  3. 通知阶段
    • notify 遍历观察者列表,调用每个 ConcreteObserver 的 update 方法。
    • ConcreteObserver 在 update 中访问主题状态并执行操作。

时序图如下:


8. C++ 语言实现

以下 C++ 代码实现了观察者模式,使用智能指针管理内存,避免内存泄漏。代码可编译运行测试。

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

// 观察者接口
class Observer {
public:
    virtual void update() = 0; // 更新方法
    virtual ~Observer() = default;
};

// 主题接口
class Subject {
public:
    virtual void attach(std::shared_ptr<Observer> observer) = 0; // 注册观察者
    virtual void detach(std::shared_ptr<Observer> observer) = 0; // 删除观察者
    virtual void notify() = 0; // 通知观察者
    virtual ~Subject() = default;
};

// 具体主题类
class ConcreteSubject : public Subject {
private:
    int state_ = 0; // 状态
    std::vector<std::shared_ptr<Observer>> observers_; // 观察者列表

public:
    void attach(std::shared_ptr<Observer> observer) override {
        observers_.push_back(observer);
    }

    void detach(std::shared_ptr<Observer> observer) override {
        auto it = std::find(observers_.begin(), observers_.end(), observer);
        if (it != observers_.end()) {
            observers_.erase(it);
        }
    }

    void notify() override {
        for (auto& observer : observers_) {
            observer->update();
        }
    }

    int getState() const {
        return state_;
    }

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

// 具体观察者类
class ConcreteObserver : public Observer {
private:
    std::shared_ptr<ConcreteSubject> subject_; // 关联的主题

public:
    ConcreteObserver(std::shared_ptr<ConcreteSubject> subject) : subject_(subject) {}

    void update() override {
        std::cout << "Observer updated. New state: " << subject_->getState() << std::endl;
    }
};

// 测试函数
int main() {
    // 创建主题和观察者
    auto subject = std::make_shared<ConcreteSubject>();
    auto observer1 = std::make_shared<ConcreteObserver>(subject);
    auto observer2 = std::make_shared<ConcreteObserver>(subject);

    // 注册观察者
    subject->attach(observer1);
    subject->attach(observer2);

    // 改变状态,触发通知
    std::cout << "Setting state to 10..." << std::endl;
    subject->setState(10); // 通知所有观察者

    // 删除一个观察者
    subject->detach(observer1);

    // 再次改变状态
    std::cout << "Setting state to 20..." << std::endl;
    subject->setState(20); // 只通知剩余观察者

    return 0;
}
9. 编译运行测试输出

使用 g++ 编译并运行:

bash 复制代码
g++ -std=c++11 -o observer_example observer_example.cpp
./observer_example

预期输出:

复制代码
Setting state to 10...
Observer updated. New state: 10
Observer updated. New state: 10
Setting state to 20...
Observer updated. New state: 20

输出表明:

  • 第一次 setState(10) 通知了两个观察者。
  • 删除 observer1 后,setState(20) 只通知了 observer2

10. 总结优缺点和改进

优点

  • 松耦合:主题和观察者通过接口交互,降低依赖。
  • 可扩展性:易于添加新观察者,无需修改主题。
  • 事件驱动:高效通知机制,避免轮询开销。
  • 符合设计原则:支持开闭原则和单一职责原则。

缺点

  • 性能问题:观察者过多时,遍历通知可能导致性能瓶颈。
  • 循环依赖风险:观察者间相互引用可能引发无限循环。
  • 状态传递不足update 方法可能无法获取状态改变原因。
  • 内存泄漏风险:若未正确解除引用,可能导致观察者无法回收(本实现使用智能指针避免了此问题)。

改进建议

  • 异步通知 :使用线程或事件队列实现异步 update,避免阻塞主题。
  • 事件对象 :在 update 方法中传递事件对象(如状态变更原因),增强信息量。
  • 弱引用管理 :在主题中使用弱引用存储观察者,防止内存泄漏(本实现已用 std::shared_ptr)。
  • 批量通知:对观察者分组,减少通知次数。
  • 使用现有库:如 C++ Boost.Signals2 或 Qt 信号槽机制,简化实现。

观察者模式是构建灵活事件系统的核心,合理使用可大幅提升代码可维护性。

相关推荐
2501_941664961 小时前
云计算与边缘计算:新时代数字化转型的双轮驱动
数据库
x***58701 小时前
GitHub星标10万+的Redis项目,使用教程
数据库·redis·github
Leon-Ning Liu1 小时前
MySQL 5.7大表索引优化实战:108GB数据建索引效率提升50%
运维·数据库·mysql
ABILI .1 小时前
Oracle导出
数据库·oracle
MSTcheng.2 小时前
【C++STL】priority_queue 模拟实现与仿函数实战
开发语言·c++
Wang's Blog2 小时前
MySQL: 数据库监控核心要素与实施策略
数据库·mysql
还有几根头发呀2 小时前
从 C++ 的角度,系统地解释 进程(Process)、线程(Thread)、协程(Coroutine) 的概念、原理、优缺点,以及常见应用场景。
c++
周杰伦fans2 小时前
依赖倒置原则(DIP)Dependency Inversion Principle
数据库·依赖倒置原则
oioihoii2 小时前
Python与C++:从哲学到细节的全面对比
c++