Observer(观察者模式)
1. 意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
在观察者模式中,有两类对象:被观察者(Subject)和观察者(Observer)。被观察者是一个具有状态的对象,当其状态发生变化时,会通知所有的观察者。观察者是一个依赖于被观察者的对象,它会接收到被观察者的通知,并进行相应的处理。
2. 适用性
《GOF设计模式:可复用面向对象软件的基础》中描述如下:
- 当一个抽象模型有两方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们各自独立地自主改变和复用。
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定对象是谁。即对象之间关系是解耦合的。
3. 实现
#include <iostream>
#include <string>
#include <vector>class Observer {
public:virtual ~Observer() {}virtual void Update(const std::string &msg) = 0;
};class NetObserver : public Observer {
public:virtual void Update(const std::string &msg) override {std::cout << "NetObserver recv msg: " << msg << std::endl;}
};class SerialObserver : public Observer {
public:virtual void Update(const std::string &msg) override {std::cout << "SerialObserver recv msg: " << msg << std::endl;}
};class Subject {
public:virtual ~Subject() {}bool Attach(Observer *ptr) {if (ptr == nullptr)return false;m_vecObserver.push_back(ptr);return true;}void Detach(const Observer *ptr) {for (auto iter = m_vecObserver.begin(); iter != m_vecObserver.end();++iter) {if (*iter != ptr)continue;m_vecObserver.erase(iter);}}void Notify(const std::string &msg) {for (Observer *ptr : m_vecObserver)ptr->Update(msg);}private:std::vector<Observer *> m_vecObserver;
};class NetSubject : public Subject {
public:void DoSomething() { Notify("NetSubject msg"); }
};class SerialSubject : public Subject {
public:void DoSomething() { Notify("SerialSubject msg"); }
};void Test() {NetObserver *net1 = new NetObserver;NetObserver *net2 = new NetObserver;SerialObserver *serial1 = new SerialObserver;SerialObserver *serial2 = new SerialObserver;NetSubject *netSub = new NetSubject;SerialSubject *serialSub = new SerialSubject;netSub->Attach(net1);netSub->DoSomething();std::cout << "------------------------------" << std::endl;netSub->Attach(net2);netSub->DoSomething();std::cout << "------------------------------" << std::endl;serialSub->Attach(serial1);serialSub->DoSomething();std::cout << "------------------------------" << std::endl;serialSub->Attach(serial2);serialSub->DoSomething();delete net1;delete net2;delete serial1;delete serial2;delete netSub;delete serialSub;
}int main() {Test();return 0;
}
执行结果
| NetObserver recv msg: NetSubject msg ------------------------------ NetObserver recv msg: NetSubject msg NetObserver recv msg: NetSubject msg ------------------------------ SerialObserver recv msg: SerialSubject msg ------------------------------ SerialObserver recv msg: SerialSubject msg SerialObserver recv msg: SerialSubject msg |
4. 优缺点
-
松耦合性:被观察者和观察者之间是松耦合的,它们之间仅通过接口或抽象类进行通信,不需要直接相互引用,可以独立变化。
-
可扩展性:可以随时增加新的观察者,而不需要修改被观察者的代码,也可以很容易地添加新的被观察者。
-
可重用性:观察者模式可以在不同的场景中重复使用,不需要重写逻辑。
-
实时更新:被观察者一旦发生变化,所有的观察者都会实时收到通知并进行相应的更新,保证了数据的实时性。
-
观察者过多:如果观察者的数量过多,通知所有观察者可能会导致性能问题,影响系统的运行效率。
-
顺序控制困难:观察者之间是一种松散的耦合关系,无法控制观察者接收通知的顺序,可能会导致一些不可预测的问题。
-
循环依赖问题:如果观察者与被观察者之间存在循环依赖,可能会导致系统出现问题。
5. 模板实现
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>class ObserverA {
public:void UpdateA(const std::string &msg) {std::cout << "ObserverA msg: " << msg << std::endl;}
};class ObserverB {
public:void UpdateB(const std::string &msg) {std::cout << "ObserverB msg: " << msg << std::endl;}
};template <typename Func> class Event {
public:Event() : m_observeId(0) {}virtual ~Event() {}int Attach(Func &&f) { return Assign(f); }int Attach(const Func &f) { return Assign(f); }void Detach(int key) { m_mapOberserve.erase(key); }template <typename... Args> void Notify(Args &&...args) {for (auto &it : m_mapOberserve) {it.second(std::forward<Args>(args)...);}}private:Event(const Event &) = delete;Event &operator=(const Event &) = delete;template <typename F> int Assign(F &&f) {m_mapOberserve.emplace(m_observeId++, std::forward<F>(f));return m_observeId - 1;}private:int m_observeId;std::unordered_map<int, Func> m_mapOberserve;
};
void Test() {Event<std::function<void(const std::string &)>> event;ObserverA oba;int key =event.Attach(std::bind(&ObserverA::UpdateA, oba, std::placeholders::_1));event.Notify("AAAAA");std::cout << "-----------------------" << std::endl;ObserverB obb;event.Attach(std::bind(&ObserverB::UpdateB, obb, std::placeholders::_1));event.Notify("AAAAABBBBB");std::cout << "-----------------------" << std::endl;event.Detach(key);event.Notify("BBBBB");
}int main() {Test();return 0;
}
执行输出
| ObserverA msg: AAAAA ----------------------- ObserverB msg: AAAAABBBBB ObserverA msg: AAAAABBBBB ----------------------- ObserverB msg: BBBBB |
相关文章:
Observer(观察者模式)
1. 意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 在观察者模式中,有两类对象:被观察者(Subject)和观察者(Observer…...
Python深度学习进阶与前沿应用:注意力机制、Transformer模型、生成式模型、目标检测算法、图神经网络、强化学习等
近年来,伴随着以卷积神经网络(CNN)为代表的深度学习的快速发展,人工智能迈入了第三次发展浪潮,AI技术在各个领域中的应用越来越广泛。为了帮助广大学员更加深入地学习人工智能领域最近3-5年的新理论与新技术࿰…...
24.1 prometheus-exporter管理
本节重点介绍 : exporter 流派 必须和探测对象部署在一起的1对多的远端探针模式 exporter管控的难点 1对1 的exporter 需要依托诸如 ansible等节点管理工具 ,所以应该尽量的少 1对1的exporter改造成探针型的通用思路 exporter 流派 必须和探测对象部署在一起的…...
【Arduino IDE安装】Arduino IDE的简介和安装详情
目录 🌞1. Arduino IDE概述 🌞2. Arduino IDE安装详情 🌍2.1 获取安装包 🌍2.2 安装详情 🌍2.3 配置中文 🌍2.4 其他配置 🌞1. Arduino IDE概述 Arduino IDE(Integrated Deve…...
『网络游戏』自适应制作登录UI【01】
首先创建项目 修改场景名字为SceneLogin 创建一个Plane面板 - 将摄像机照射Plane 新建游戏启动场景GameRoot 新建空节点重命名为GameRoot 在子级下创建Canvas 拖拽EventSystem至子级 在Canvas子级下创建空节点重命名为LoginWnd - 即登录窗口 创建公告按钮 创建字体文本 创建输入…...
用Manim简单解释奇异值分解(SVD)和图像处理方面的应
一,介绍 奇异值分解(SVD)是一种重要的矩阵分解技术,在统计学、信号处理和机器学习等领域有广泛应用。对于任意给定的矩阵 A(可以是任意形状的矩阵),SVD将其分解为三个特定的矩阵的乘积&#x…...
红外变电站分割数据集,标注为json格式,总共有5类,避雷器(289张),绝缘子(919张),电流互感器(413张),套管(161张),电压互感器(153张)
红外变电站分割数据集,标注为json格式,总共有5类 避雷器(289张),绝缘子(919张),电流互感器(413张),套管(161张)࿰…...
HBase 性能优化 详解
HBase 是基于 Hadoop HDFS 之上的分布式 NoSQL 数据库,具有高伸缩性和强大的读写能力。然而,由于其分布式架构和复杂的数据存储模式,在高并发、大规模数据场景下,HBase 性能优化至关重要。从底层原理和源代码层面理解 HBase 的特性…...
杭电2041-2050
2041 这里进入递归专题了 #include<bits/stdc.h> #include<iostream> //简单递归 using namespace std; long long int M[45]; int main() {int n;M[1]1;M[2]1;for(int i3;i<45;i){M[i]M[i-1]M[i-2];}while(cin>>n){while(n--){int m;cin>>m;cout…...
Ambari搭建Hadoop集群 — — 问题总结
Ambari搭建Hadoop集群 — — 问题总结 一、部署教程: 参考链接:基于Ambari搭建大数据分析平台-CSDN博客 二、问题总结: 1. VMwear Workstation 查看网关 2. 资源分配 参考: 硬盘:master(29 GBÿ…...
如何用python抓取豆瓣电影TOP250
1.如何获取网站信息? (1)调用requests库、bs4库 #检查库是否下载好的方法:打开终端界面(terminal)输入pip install bs4, 如果返回的信息里有Successfully installed bs4 说明安装成功(request…...
鸽笼原理与递归 - 离散数学系列(四)
目录 1. 鸽笼原理 鸽笼原理的定义 鸽笼原理的示例 鸽笼原理的应用 2. 递归的定义与应用 什么是递归? 递归的示例 递归与迭代的对比 3. 实际应用 鸽笼原理的实际应用 递归的实际应用 4. 例题与练习 例题1:鸽笼原理应用 例题2:递归…...
Ubuntu 20.04常见配置(含yum源替换、桌面安装、防火墙设置、ntp配置)
Ubuntu 20.04常见配置 1. yum源配置2. 安装桌面及图形化2.1 安装图形化桌面2.1.1 选择安装gnome桌面2.1.2 选择安装xface桌面 2.2 安装VNC-Server 3. ufw防火墙策略4. 时区设置及NTP时间同步4.1 时区设置4.2 NTP安装及时间同步4.2.1 服务端(例:172.16.32…...
AI学习指南深度学习篇-生成对抗网络的基本原理
AI学习指南深度学习篇-生成对抗网络的基本原理 引言 生成对抗网络(Generative Adversarial Networks, GANs)是近年来深度学习领域的一个重要研究方向。GANs通过一种创新的对抗训练机制,能够生成高质量的样本,其应用范围广泛&…...
什么是网络安全
网络安全是指通过采取必要措施,防范对网络的攻击、侵入、干扰、破坏和非法使用以及意外事故,使网络处于稳定可靠运行的状态,以及保障网络数据的完整性、保密性、可用性的能力。 网络安全涉及多个层面,包括硬件、软件及其系统中数…...
Redis list 类型
list类型 类型介绍 列表类型 list 相当于 数组或者顺序表 list内部的编码方式更接近于 双端队列 ,支持头插 头删 尾插 尾删。 需要注意的是,Redis的下标支持负数下标。 比如数组大小为5,那么要访问下标为 -2 的值可以理解为访问 5 - 2 3 …...
Linux更改固定IP地址
1.VMware里更改虚拟网络 一: 二: 三:确定就好了 2.修改Linux系统的固定IP 一:进入此文件 效果如下: 执行以下命令: 此时IP已更改 3.远程连接 这个是前提!!! 更改网络编辑器后网络适配器可能会修改,我就是遇着这个,困住我了一会 一:可以以主机IP对应连接 连接成功 二:主机名连…...
Qt+大恒相机回调图片刷新使用方式
一、前言 上篇文章介绍了如何调用大恒SDK获得回调图片,这篇介绍如何使用这些图片并刷新到界面上。考虑到相机的帧率很高,比如200fps是很高的回调频率。那么我们的刷新频率是做不到这么快,也没必要这么快。一般刷新在60帧左右就够了。 二、思路…...
Docker 环境下 PostgreSQL 监控实战:从 Exporter 到 Prometheus 的部署详解
Docker 环境下 PostgreSQL 监控实战:从 Exporter 到 Prometheus 的部署详解 文章目录 Docker 环境下 PostgreSQL 监控实战:从 Exporter 到 Prometheus 的部署详解一 节点简述二 节点监控部署1)创建 PostgreSQL 的 exporter 账号2)…...
构建带有调试符号的srsRAN 4G
### 构建带有调试符号 首先确保已下载srsRAN 4G,并已创建并导航至构建文件夹: bash git clone https://github.com/srsran/srsran_4g.git cd srsRAN_4G mkdir build cd build 若srsRAN 4G已构建完成,应清除原有构建文件夹后继续。 可以使…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...
起重机起升机构的安全装置有哪些?
起重机起升机构的安全装置是保障吊装作业安全的关键部件,主要用于防止超载、失控、断绳等危险情况。以下是常见的安全装置及其功能和原理: 一、超载保护装置(核心安全装置) 1. 起重量限制器 功能:实时监测起升载荷&a…...
