C++软件设计模式之观察者模式

观察者模式的动机意图

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都能够自动收到通知并进行相应的更新。

动机意图:

  • 解耦:观察者模式的动机在于解耦对象之间的关系。具体来说,它让一个对象(被观察者或主题)不需要知道具体有哪些对象(观察者)依赖于它,从而降低了对象之间的耦合度。
  • 自动通知:当被观察者的状态发生变化时,所有观察者都会自动收到通知,并执行相应的操作。这种机制使得系统更加灵活,易于扩展和维护。

适用场合

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

  1. 当一个对象的状态变化需要通知其他对象,并且你不知道这些对象的具体数量和类型时。例如,图形用户界面(GUI)中的按钮点击事件,可以通知多个监听器。
  2. 当需要在不修改被观察者代码的情况下,动态添加或删除观察者时。这种情况下,观察者模式可以实现松耦合的设计。
  3. 当一个对象的状态变化会影响多个其他对象,并且这些对象需要根据状态变化自动更新时。例如,股票价格的变化会影响多个投资者的策略。

观察者模式的变体

观察者模式有几种常见的变体,具体取决于通知机制、更新方式以及观察者的管理方式。以下是几种常见的变体:

  1. 推模型(Push Model)

    • 特点:被观察者主动将状态变化的信息推送给所有观察者。
    • 优点:观察者不需要知道被观察者的内部结构,只需要处理推送过来的数据。
    • 缺点:被观察者需要知道观察者的接口,可能会导致观察者和被观察者之间的耦合。
  2. 拉模型(Pull Model)

    • 特点:被观察者仅通知观察者状态已发生变化,观察者需要主动去拉取被观察者的具体状态。
    • 优点:观察者可以只拉取自己感兴趣的部分状态,减少不必要的数据传输。
    • 缺点:观察者需要了解被观察者的内部结构。
  3. 事件机制(Event-based Model)

    • 特点:被观察者通过事件通知观察者,观察者可以订阅特定类型的事件。
    • 优点:可以处理更复杂的事件类型,支持多种事件的订阅和处理。
    • 缺点:实现复杂度较高。
  4. 观察者队列(Observer Queue)

    • 特点:使用队列来管理观察者,当被观察者状态变化时,将通知放入队列中,观察者从队列中取出通知进行处理。
    • 优点:可以实现异步通知,提高系统的并发性能。
    • 缺点:可能引入额外的复杂性和延迟。
  5. 弱引用观察者(Weak Reference Observer)

    • 特点:使用弱引用(Weak Reference)来管理观察者,防止观察者对象未被释放时导致的内存泄漏。
    • 优点:可以避免内存泄漏问题。
    • 缺点:需要额外的机制来管理弱引用。

总结

观察者模式通过解耦对象之间的关系,提供了一种灵活且易于扩展的通知机制。它的变体如推模型、拉模型、事件机制等,使得它在不同的场景中可以有不同的实现方式。基于观察者模式的特点,发布-订阅模式、事件驱动架构、MVC模式等软件架构模式在现代软件开发中得到了广泛的应用。这些架构模式不仅提高了系统的灵活性和可维护性,还使得系统在面对变化时能够更加从容应对。

下面是观察者模式的不同变体的C++代码示例,包括推模型、拉模型、事件机制、观察者队列和弱引用观察者。

1. 推模型(Push Model)

在推模型中,被观察者将状态变化的信息主动推送给所有观察者。

示例代码
#include <iostream>
#include <vector>
#include <string>// 观察者接口
class Observer {
public:virtual void update(const std::string& message) = 0;virtual ~Observer() = default;
};// 具体观察者
class ConcreteObserver : public Observer {
public:void update(const std::string& message) override {std::cout << "Observer received message: " << message << std::endl;}
};// 被观察者接口
class Subject {
public:virtual void attach(Observer* observer) = 0;virtual void detach(Observer* observer) = 0;virtual void notify(const std::string& message) = 0;virtual ~Subject() = default;
};// 具体被观察者
class ConcreteSubject : public Subject {
private:std::vector<Observer*> observers;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(const std::string& message) override {for (Observer* observer : observers) {observer->update(message);}}void changeState(const std::string& message) {std::cout << "Subject state changed. Notifying observers..." << std::endl;notify(message);}
};int main() {ConcreteSubject subject;ConcreteObserver observer1;ConcreteObserver observer2;subject.attach(&observer1);subject.attach(&observer2);subject.changeState("Hello Observers!");subject.detach(&observer1);subject.changeState("Hello again!");return 0;
}

2. 拉模型(Pull Model)

在拉模型中,被观察者仅通知观察者状态已发生变化,观察者需要主动去拉取被观察者的具体状态。

示例代码
#include <iostream>
#include <vector>
#include <string>// 被观察者接口
class Subject {
public:virtual std::string getState() const = 0;virtual void notify() = 0;virtual void setState(const std::string& state) = 0;virtual void attach(class Observer* observer) = 0;virtual void detach(class Observer* observer) = 0;virtual ~Subject() = default;
};// 具体被观察者
class ConcreteSubject : public Subject {
private:std::string state;std::vector<class Observer*> observers;public:std::string getState() const override {return state;}void setState(const std::string& state) override {this->state = state;notify();}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();}}
};// 观察者接口
class Observer {
protected:Subject* subject;public:Observer(Subject* subject) : subject(subject) {}virtual void update() = 0;virtual ~Observer() = default;
};// 具体观察者
class ConcreteObserver : public Observer {
public:ConcreteObserver(Subject* subject) : Observer(subject) {}void update() override {std::string state = subject->getState();std::cout << "Observer received state: " << state << std::endl;}
};int main() {ConcreteSubject subject;ConcreteObserver observer1(&subject);ConcreteObserver observer2(&subject);subject.attach(&observer1);subject.attach(&observer2);subject.setState("State 1");subject.detach(&observer1);subject.setState("State 2");return 0;
}

3. 事件机制(Event-based Model)

在事件机制中,被观察者通过事件通知观察者,观察者可以订阅特定类型的事件。

示例代码
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <functional>// 事件类型
enum class EventType {StateChanged,DataUpdated
};// 事件
class Event {
public:EventType type;std::string data;Event(EventType type, const std::string& data) : type(type), data(data) {}
};// 观察者接口
class Observer {
public:virtual void handleEvent(const Event& event) = 0;virtual ~Observer() = default;
};// 具体观察者
class ConcreteObserver : public Observer {
public:void handleEvent(const Event& event) override {std::cout << "Observer received event of type: " << static_cast<int>(event.type)<< " with data: " << event.data << std::endl;}
};// 被观察者接口
class Subject {
private:std::map<EventType, std::vector<Observer*>> eventObservers;public:void attach(EventType type, Observer* observer) {eventObservers[type].push_back(observer);}void detach(EventType type, Observer* observer) {auto& observers = eventObservers[type];observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notify(const Event& event) {auto& observers = eventObservers[event.type];for (Observer* observer : observers) {observer->handleEvent(event);}}void changeState(const std::string& state) {std::cout << "Subject state changed." << std::endl;notify(Event(EventType::StateChanged, state));}void updateData(const std::string& data) {std::cout << "Subject data updated." << std::endl;notify(Event(EventType::DataUpdated, data));}
};int main() {Subject subject;ConcreteObserver observer1;ConcreteObserver observer2;subject.attach(EventType::StateChanged, &observer1);subject.attach(EventType::DataUpdated, &observer2);subject.changeState("New State");subject.updateData("New Data");subject.detach(EventType::StateChanged, &observer1);subject.changeState("Another State");return 0;
}

4. 观察者队列(Observer Queue)

在观察者队列中,使用队列来管理观察者,当被观察者状态变化时,将通知放入队列中,观察者从队列中取出通知进行处理。

示例代码
#include <iostream>
#include <queue>
#include <string>
#include <mutex>
#include <thread>
#include <condition_variable>// 通知类型
class Notification {
public:virtual void process() = 0;virtual ~Notification() = default;
};// 具体通知
class ConcreteNotification : public Notification {
private:std::string message;public:ConcreteNotification(const std::string& message) : message(message) {}void process() override {std::cout << "Processing notification: " << message << std::endl;}
};// 观察者队列
class ObserverQueue {
private:std::queue<Notification*> queue;std::mutex mutex;std::condition_variable cv;public:void enqueue(Notification* notification) {std::lock_guard<std::mutex> lock(mutex);queue.push(notification);cv.notify_one();}void processQueue() {while (true) {std::unique_lock<std::mutex> lock(mutex);cv.wait(lock, [this] { return !queue.empty(); });Notification* notification = queue.front();queue.pop();lock.unlock();notification->process();delete notification;}}
};int main() {ObserverQueue queue;std::thread worker([&queue]() { queue.processQueue(); });queue.enqueue(new ConcreteNotification("Notification 1"));queue.enqueue(new ConcreteNotification("Notification 2"));queue.enqueue(new ConcreteNotification("Notification 3"));worker.join();return 0;
}

5. 弱引用观察者(Weak Reference Observer)

在弱引用观察者中,使用弱引用(Weak Reference)来管理观察者,防止观察者对象未被释放时导致的内存泄漏。

示例代码
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <functional>// 观察者接口
class Observer {
public:virtual void update(const std::string& message) = 0;virtual ~Observer() = default;
};// 具体观察者
class ConcreteObserver : public Observer {
public:void update(const std::string& message) override {std::cout << "Observer received message: " << message << std::endl;}
};// 被观察者接口
class Subject {
public:virtual void attach(std::weak_ptr<Observer> observer) = 0;virtual void notify(const std::string& message) = 0;virtual ~Subject() = default;
};// 具体被观察者
class ConcreteSubject : public Subject {
private:std::vector<std::weak_ptr<Observer>> observers;public:void attach(std::weak_ptr<Observer> observer) override {observers.push_back(observer);}void notify(const std::string& message) override {observers.erase(std::remove_if(observers.begin(), observers.end(),[](const std::weak_ptr<Observer>& observer) {return observer.expired();}), observers.end());for (auto& observer : observers) {if (auto sharedObserver = observer.lock()) {sharedObserver->update(message);}}}void changeState(const std::string& message) {std::cout << "Subject state changed. Notifying observers..." << std::endl;notify(message);}
};int main() {ConcreteSubject subject;auto observer1 = std::make_shared<ConcreteObserver>();auto observer2 = std::make_shared<ConcreteObserver>();subject.attach(observer1);subject.attach(observer2);subject.changeState("Hello Observers!");observer1.reset(); // 释放 observer1subject.changeState("Hello again!");return 0;
}

总结

以上是观察者模式的几种不同变体的C++代码示例。每种变体都有其适用的场景和优缺点,开发者可以根据具体的需求选择合适的实现方式。观察者模式的核心思想是解耦对象之间的关系,并通过通知机制实现对象之间的通信。

基于观察者模式特点的软件架构模式

观察者模式的特性使得它在许多软件架构中都有重要的应用,以下是几种基于观察者模式特点的软件架构模式:

  1. 发布-订阅模式(Publish-Subscribe Pattern)

    • 特点:发布-订阅模式是观察者模式的一种扩展,它将消息的发送者(发布者)和接收者(订阅者)完全解耦。发布者不需要知道订阅者的存在,订阅者也不需要知道发布者的存在。
    • 应用:常见的应用场景包括消息队列系统(如 Kafka、RabbitMQ)、事件驱动系统等。
    • 优点:系统组件之间的耦合度极低,适合构建分布式系统。
  2. 事件驱动架构(Event-Driven Architecture)

    • 特点:事件驱动架构是一种基于事件的架构模式,系统中的组件通过事件进行通信。事件的产生和处理通常基于观察者模式。
    • 应用:常见的应用场景包括图形用户界面、分布式系统、微服务架构等。
    • 优点:系统组件可以异步处理事件,提高系统的可扩展性和响应性。
  3. 模型-视图-控制器(MVC)模式

    • 特点:MVC模式将应用程序分为三个部分:模型(Model)、视图(View)和控制器(Controller)。模型是观察者模式中的被观察者,视图是观察者。当模型的状态发生变化时,视图会自动更新。
    • 应用:常见的应用场景包括Web应用程序、桌面应用程序等。
    • 优点:模型和视图之间的解耦,使得系统更易于维护和扩展。
  4. 观察者-被观察者链(Observer-Observable Chain)

    • 特点:在这种架构中,观察者和被观察者可以形成一个链式结构,每个被观察者可以同时是观察者。
    • 应用:常见的应用场景包括事件传播链、责任链模式等。
    • 优点:可以实现复杂的事件传播和处理机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

评分模型在路网通勤习惯分析中的应用——启发式搜索(2)

接上文《评分模型在路网通勤习惯分析中的应用——提出问题&#xff08;1&#xff09;》&#xff0c;本文内容主要针对上文提出的问题解决思路&#xff0c;详细讨论每一步骤中的具体处理措施。 4、问题的详细解决过程 4.1、对地图数据进行结构化 地图的本质是一种有向加权图结…

Mysql(MGR)和ProxySQL搭建部署-Kubernetes版本

一、Mysql(MGR) 1.1 statefulSet.yaml apiVersion: apps/v1 kind: StatefulSet metadata:labels:app: mysqlname: mysqlnamespace: yihuazt spec:replicas: 3serviceName: mysql-headlessselector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:affinity:p…

【C语言】库函数常见的陷阱与缺陷(三):内存分配函数[3]--realloc

C语言中的realloc函数用于重新分配先前分配的内存块的大小,并可能更改其位置。然而,realloc函数的使用也存在一些陷阱与缺陷。 一、功能与常见用法 realloc 函数用于调整之前通过 malloc 或 calloc 分配的内存块的大小。它既可以增加也可以减少内存的大小,具体取决于请求的…

Lecture 17

10’s Complement Representation 主要内容&#xff1a; 1. 10’s 补码表示: • 10’s 补码表示法需要指定表示的数字位数&#xff08;用 n 表示&#xff09;。 • 表示的数字取决于 n 的位数&#xff0c;这会影响具体数值的解释。 2. 举例: • 如果采用 3 位补码&…

【Hackthebox 中英 Write-Up】Web Request | 分析 HTTP 请求和响应

欢迎来到我的writeup分享&#xff01;我希望大家不要只关注结果或答案&#xff0c;而是通过耐心阅读&#xff0c;尝试逆向工程理解背后的运作原理。在这里&#xff0c;你不仅能找到解题的思路&#xff0c;还能学到更多与Hack The Box等平台相关的技术和技巧&#xff0c;期待与你…

linux 点对点语音通话及直播推流实践一: linux USB声卡或耳机 基本配置

inux USB声卡或耳机 基本配置 工具安装查看设备录放音操作录音放音声音配置获取控制信息音量配置本文介绍 linux下alsa声音原件 工具使用方法,包括设备查询、声卡基本配置、录音放音等。 保证 alsa套件可正常操作和配置声卡,是实现SIP语音通话、音视频 采集及推拉流功能的基础…

《优势谈判》——阅读笔记

投入 思想准备&#xff1a;一个谈判是要双赢的&#xff0c;至少需要让对手这么认为&#xff1b;进行一场谈判&#xff0c;需要看到对面是否真的愿意和你谈判 谈判技巧 永远不要接受首次报份&#xff1b;如果对方临时变更了要求&#xff0c;则可以通过立刻要求对方做补偿等方…

PPT画图——如何设置导致图片为600dpi

winr&#xff0c;输入regedit打开注册表 按路径找&#xff0c;HKEY_CURRENT_USER\Software\Microsoft\Office\XX.0\PowerPoint\Options&#xff08;xx为版本号&#xff0c;16.0 or 15.0或则其他&#xff09;。名称命名&#xff1a;ExportBitmapResolution 保存即可&#xff0c;…

高性能网络框架--fstack

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 Fstack 是一个高性能的网络框架&#xff0c;主要用于构建高性能的网络应用程序&#xff0c;特别是在处理大量并发连接时。它基于 Linux 的 epoll 机制&#xff0c;使用了多线程和事件驱动的编程模型。以下是对 …

电商项目-数据同步解决方案(三)商品上架同步更新ES索引库

一、 需求分析和业务逻辑 主要应用技术有&#xff1a;Feign远程调用&#xff0c; 消息队列-RabbitMQ &#xff0c;分布式搜索引擎-ElasticSearch&#xff0c;Eureka&#xff0c;Canal 商品上架将商品的sku列表导入或者更新索引库。 数据监控微服务需要定义canal监听器&#x…

Unity游戏环境交互系统

概述 交互功能使用同一个按钮或按钮列表&#xff0c;在不同情况下显示不同的内容&#xff0c;按下执行不同的操作。 按选项个数分类 环境交互系统可分为两种&#xff0c;单选项交互&#xff0c;一般使用射线检测&#xff1b;多选项交互&#xff0c;一般使用范围检测。第一人…

前端正在被“锈”化

jeff Atwood 在 2007 年说&#xff1a;"any application that can be writen in JavaScript , willeventually be written in JavaScript"&#xff0c;翻译过来就是&#xff1a;“任何可以使用 JavaScript 来编写的应用&#xff0c;并最终也会由 JavaScript 编写”&a…

穷举vs暴搜vs深搜vs回溯vs剪枝_全排列_子集

46. 全排列 递归解决&#xff1a;一开始选一个数&#xff0c;递归进入下一层再选一个新的数&#xff0c;直到到最后一个数。反会上一层遍历其它数。 每次递归到叶子节点就找到了一种组合&#xff0c;思路有了具体怎么实现&#xff1f; 1.怎么记录每条路径&#xff1f; 定义一个…

【openGauss】正则表达式次数符号“{}“在ORACLE和openGauss中的差异

一、前言 正则作为一种常用的字符串处理方式&#xff0c;在各种开发语言&#xff0c;甚至数据库中&#xff0c;都有自带的正则函数。但是正则函数有很多标准&#xff0c;不同标准对正则表达式的解析方式不一样&#xff0c;本次在迁移一个ORACLE数据库到openGauss时发现了一个关…

【代码随想录|完全背包问题】

518.零钱兑换|| 题目链接&#xff1a;518. 零钱兑换 II - 力扣&#xff08;LeetCode&#xff09; 这里求的是组合数&#xff0c;就是不强调元素排列的顺序&#xff0c;211和121是同一个数那种&#xff0c;要先遍历物品&#xff0c;这样的话我算出来的每个值才是按顺序121&…

go语言的成神之路-筑基篇-gin常用功能

第一节-gin参数绑定 目录 第一节-?gin参数绑定 ShouldBind简要概述 功能&#xff1a; 使用场景&#xff1a; 可能的错误&#xff1a; 实例代码 效果展示 第二节-gin文件上传 选择要上传的文件 选择要上传的文件。 效果展示? 代码部分 第三节-gin请求重定向 第…

Qt 12.28 day3

作业&#xff1a; 1】 思维导图 2】 在登录界面的登录取消按钮进行以下设置&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&a…

mybatis-plus 用法总结

MyBatis-Plus&#xff08;简称 MP&#xff09;是 MyBatis 的增强工具&#xff0c;旨在简化开发者的 CRUD 操作。它在 MyBatis 的基础上提供了更多的功能和便利性&#xff0c;如代码生成器、分页插件、性能分析插件等&#xff0c;使开发者能够更高效地进行数据库操作。MyBatis-P…

Rust: enum 和 i32 的区别和互换

在Rust编程语言中&#xff0c;enum&#xff08;枚举&#xff09;和i32是两种不同类型的数据结构&#xff0c;它们各自有不同的用途和特性。 i32 i32是一个32位的有符号整数类型。它用于存储整数值&#xff0c;范围从-2,147,483,648到2,147,483,647。i32是Rust中的基本数据类型…

迁移学习 详解及应用示例

简介&#xff1a; 迁移学习是一种机器学习技术&#xff0c;其核心思想是利用在一个任务上已经学到的知识&#xff08;源任务&#xff1a;任务已经有一个训练好的模型&#xff0c;然后我们将这个模型的某些部分或知识迁移到一个新的但相关的“目标任务”上。&#xff09;来帮助解…