重拾设计模式--观察者模式
文章目录
- 观察者模式(Observer Pattern)概述
- 观察者模式UML图
- 作用:
- 实现对象间的解耦
- 支持一对多的依赖关系
- 易于维护和扩展
- 观察者模式的结构
- 抽象主题(Subject):
- 具体主题(Concrete Subject):
- 抽象观察者(Observer):
- 具体观察者(Concrete Observer):
- C++ 代码示例1
- C++代码示例2
观察者模式(Observer Pattern)概述
定义:
观察者模式又被称作发布 - 订阅模式,它是一种行为型设计模式。在这种模式中,存在一个被观察的对象(主题,Subject)以及多个对该主题状态变化感兴趣的观察者(Observer)对象。当主题的状态发生改变时,它会主动通知所有已注册的观察者,观察者们可以根据收到的通知做出相应的响应,从而实现对象之间的一种松耦合的交互关系。
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听同一主题对象,当这个主题对象发生变化时,能够通知到所有观察者,使他们能够自动更新自己
观察者模式UML图
作用:
实现对象间的解耦
主题对象和观察者对象之间相互依赖的关系比较松散,主题只负责在自身状态改变时通知观察者,而不用关心观察者具体会做什么操作;观察者也只需要关注主题的通知,不用了解主题内部状态变化的具体实现细节,这样使得它们可以独立地进行修改和扩展,降低了代码的耦合度。
支持一对多的依赖关系
一个主题可以有多个观察者关注它的状态变化,当主题状态更新时,能同时通知到所有相关的观察者,方便实现一些需要多方响应的业务场景,比如在一个股票交易系统中,当某支股票价格变化(主题状态改变),多个关注该股票的股民客户端(观察者)都能及时收到通知并做出相应决策。
易于维护和扩展
如果要新增观察者或者改变主题的通知逻辑等,相对来说比较容易实现,对其他部分的代码影响较小,能够灵活应对业务需求的变化。
观察者模式的结构
抽象主题(Subject):
它定义了注册、移除观察者以及通知观察者等方法的接口,维护了一个观察者列表,用于记录所有注册的观察者对象,当自身状态改变时,通过调用通知方法来告知所有观察者。
具体主题(Concrete Subject):
实现了抽象主题中定义的接口,具体管理观察者的注册和移除操作,并且在自身内部状态发生变化时,按照约定的通知机制去通知所有已注册的观察者。它持有自身实际的业务数据和状态信息。
抽象观察者(Observer):
定义了一个更新(update)方法的接口,当接收到主题的通知时,具体的观察者实现类会通过这个方法来执行相应的业务逻辑,以对主题状态变化做出响应。
具体观察者(Concrete Observer):
实现了抽象观察者中定义的更新方法,在该方法中编写具体的业务逻辑,明确在收到主题通知后自身要进行的操作,比如更新界面显示、进行数据记录等操作。
C++ 代码示例1
以下是一个简单的基于观察者模式的示例代码,模拟一个气象站发布天气数据,多个客户端(观察者)接收并处理天气信息的场景:
#include <iostream>
#include <vector>// 抽象观察者
class Observer
{
public:virtual void update(float temperature, float humidity, float pressure) = 0;
};// 抽象主题
class Subject
{
public:virtual void registerObserver(Observer* o) = 0;virtual void removeObserver(Observer* o) = 0;virtual void notifyObservers() = 0;
};// 具体主题,气象站数据
class WeatherData : public Subject
{
private:float temperature;float humidity;float pressure;std::vector<Observer*> observers;
public:void registerObserver(Observer* o) {observers.push_back(o);}void removeObserver(Observer* o){for (std::vector<Observer*>::iterator it = observers.begin(); it!= observers.end(); ++it){if (*it == o){observers.erase(it);break;}}}void notifyObservers(){for (std::vector<Observer*>::iterator it = observers.begin(); it!= observers.end(); ++it){(*it)->update(temperature, humidity, pressure);}}void setMeasurements(float temperature, float humidity, float pressure){this->temperature = temperature;this->humidity = humidity;this->pressure = pressure;notifyObservers();}
};// 具体观察者,例如手机客户端显示天气数据
class MobileApp : public Observer
{
private:float temperature;float humidity;float pressure;
public:void update(float temperature, float humidity, float pressure){this->temperature = temperature;this->humidity = humidity;this->pressure = pressure;display();}void display(){std::cout << "Mobile App: Temperature = " << temperature << "°C, Humidity = " << humidity << "%, Pressure = " << pressure << "hPa" << std::endl;}
};// 另一个具体观察者,网页端显示天气数据
class WebPage : public Observer
{
private:float temperature;float humidity;float pressure;
public:void update(float temperature, float humidity, float pressure){this->temperature = temperature;this->humidity = humidity;this->pressure = pressure;showData();}void showData() {std::cout << "Web Page: Temperature = " << temperature << "°C, Humidity = " << humidity << "%, Pressure = " << pressure << "hPa" << std::endl;}
};int main()
{WeatherData weatherData;MobileApp mobileApp;WebPage webPage;weatherData.registerObserver(&mobileApp);weatherData.registerObserver(&webPage);weatherData.setMeasurements(25.0, 60.0, 1013.0);weatherData.removeObserver(&mobileApp);weatherData.setMeasurements(26.0, 55.0, 1015.0);return 0;
}
在上述代码中:
Observer是抽象观察者,规定了update方法接口,用于接收主题状态变化的通知并处理。
Subject是抽象主题,定义了注册、移除观察者以及通知观察者等相关接口。
WeatherData作为具体主题,管理着观察者列表,当调用setMeasurements方法改变内部天气数据状态时,会通过notifyObservers方法通知所有已注册的观察者。
MobileApp和WebPage是具体观察者,实现了update方法,在接收到气象站(主题)的通知后,各自通过不同的显示方法(display和showData)来展示更新后的天气数据。在main函数中演示了观察者的注册、主题状态更新通知以及观察者移除等操作流程,体现了观察者模式的基本应用场景。
C++代码示例2
//观察者
#include<iostream>
#include<list>
using namespace std;
//观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,
//使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新class USER
{
public:virtual void Update(){}
};
class USERLI:public USER
{void Update(){cout<<"我是小李,我去看华为博客了"<<endl;}
};class USERWANG:public USER
{void Update(){cout<<"我是小王,我去看华为博客了"<<endl;}
};class BLOG
{
public:list<USER*> m_list;//保存观察者(就是关注我博客的用户)//记录观察者void AddUser(USER *p_user){m_list.push_back(p_user);}void ReMoveUser(USER *p_user){m_list.remove(p_user);}//开始通知所有我的观察者void NotiFy(){list<USER*>::iterator itr = m_list.begin();for(;itr!=m_list.end();++itr){(*itr)->Update();}}
};//华为博客
class BLOG_HUAWEI:public BLOG
{
public:void WriteBlogOk(){NotiFy();}
};int main()
{BLOG_HUAWEI huawei;USERLI *xiaoli = new USERLI();USERWANG *xiaowang =new USERWANG();huawei.AddUser(xiaowang);huawei.AddUser(xiaoli);huawei.WriteBlogOk();//这个时间点触发通知return 0;
}
相关文章:

重拾设计模式--观察者模式
文章目录 观察者模式(Observer Pattern)概述观察者模式UML图作用:实现对象间的解耦支持一对多的依赖关系易于维护和扩展 观察者模式的结构抽象主题(Subject):具体主题(Concrete Subject…...

Vue.js前端框架教程7:Vue计算属性和moment.js
文章目录 计算属性(Computed Properties)基本用法缓存机制计算属性 vs 方法使用场景计算属性的 setter 和 getter结论Moment.js 进行时间处理1. 安装 Moment.js2. 在 Vue 组件中引入 Moment.js3. 在全局使用 Moment.js4. 使用 Vue 插件的方式引入 Moment.js5. 常用日期格式化…...

【游戏设计原理】22 - 石头剪刀布
一、游戏基础:拳头、掌心、分指 首先,石头剪刀布(又名“Roshambo”)看似简单,实际上可是个“深藏玄机”的零和博弈(听起来很高深,其实就是输赢相抵消的意思)。游戏中有三种手势&…...

3-Gin 渲染 --[Gin 框架入门精讲与实战案例]
在 Gin 框架中,渲染指的是将数据传递给模板,并生成 HTML 或其他格式的响应内容。Gin 支持多种类型的渲染,包括 String HTML、JSON、XML 等。 String 渲染 在 Gin 框架中,String 渲染方法允许你直接返回一个字符串作为 HTTP 响应…...

python小课堂(一)
基础语法 1 常量和表达式2 变量和类型2.1 变量是什么2.2 变量语法 3 变量的类型3.1 动态类型特性 4 注释4.1注释是什么 5 输入输出5.1 print的介绍5.2 input 6 运算符6.1 算术运算符在这里插入图片描述6.2 关系运算符6.3 逻辑运算符6.4赋值运算符 1 常量和表达式 在print()中可…...

GESP202309 二级【小杨的 X 字矩阵】题解(AC)
》》》点我查看「视频」详解》》》 [GESP202309 二级] 小杨的 X 字矩阵 题目描述 小杨想要构造一个 的 X 字矩阵( 为奇数),这个矩阵的两条对角线都是半角加号 ,其余都是半角减号 - 。例如,一个 5 5 5 \times 5 5…...

@PostConstruct注解解释!!!!
PostConstruct 注解修饰的方法是在 Bean 完成初始化后自动调用的。它是 Java EE 和 Spring 中的一种机制,用于在 Bean 被创建并依赖注入完成后,执行一些初始化的操作。 具体触发时机: 依赖注入完成后:首先,Spring 容器…...

laya游戏引擎中打包之后图片模糊
如下图正常运行没问题,打包之后却模糊 纹理类型中的默认类型都是精灵纹理,改为默认值即可。注意:要点击“应用”才可有效。精灵纹理类型会对图片进行渲染处理,而默认值 平面类型不会处理图片。...

【数据结构练习题】链表与LinkedList
顺序表与链表LinkedList 选择题链表面试题1. 删除链表中等于给定值 val 的所有节点。2. 反转一个单链表。3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。4. 输入一个链表,输出该链…...

[项目代码] YOLOv8 遥感航拍飞机和船舶识别 [目标检测]
项目代码下载链接 <项目代码>YOLO 遥感航拍飞机和船舶识别<目标检测>https://download.csdn.net/download/qq_53332949/90163939YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为…...

移动魔百盒中的 OpenWrt作为旁路由 安装Tailscale并配置子网路由实现在外面通过家里的局域网ip访问内网设备
移动魔百盒中的 OpenWrt作为旁路由 安装Tailscale并配置子网路由实现在外面通过家里的局域网ip访问内网设备 一、前提条件 确保路由器硬件支持: OpenWrt 路由器需要足够的存储空间和 CPU 性能来运行 Tailscale。确保设备架构支持 Tailscale 二进制文件,例…...

JVM对象分配内存如何保证线程安全?
大家好,我是锋哥。今天分享关于【JVM对象分配内存如何保证线程安全?】面试题。希望对大家有帮助; JVM对象分配内存如何保证线程安全? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在JVM中,对象的内存分配…...

ArcGIS计算土地转移矩阵
在计算土地转移矩阵时,最常使用的方法就是在ArcGIS中将土地利用栅格数据转为矢量,然后采用叠加分析计算,但这种方法计算效率低。还有一种方法是采用ArcGIS中的栅格计算器,将一个年份的地类编号乘以个100或是1000再加上另一个年份的…...

数据库 MYSQL的概念
数据库的概念 数据库是按照数据结 构来组织、存储和管理数据的系统,它允许用户高效地存储、检索、更新和管理数据 database:用来组织,存储,管理数据的仓库 数据库的管理系统:DBMS,实现对数据的有效储值&am…...

Node.js后端程序打包问题汇总(webpack、rsbuild、fastify、knex、objection、sqlite3、svg-captcha)
背景说明 场景 使用 node.js 进行后端开发,部署时通常需要打包为单文件,然后放到服务器运行。 这里记录我在打包过程中,碰到的各类问题及解决方案,希望能够帮助到更多道友😄 提示 此文持续更新,可以收藏⭐…...

部署 Apache Samza 和 Apache Kafka
部署 Apache Samza 和 Apache Kafka 的流处理系统可以分为以下几个步骤,涵盖环境准备、部署细节和生产环境的优化。 1. 环境准备 硬件要求 Kafka Broker:至少 3 台服务器,建议每台服务器配备 4 核 CPU、16GB 内存和高速磁盘。Samza 部署节点:根据任务规模,至少准备 2 台…...

xiaomiR4c openwrt
文章目录 openwrt 安装openwrt 配置开启WiFi 救砖minieap编译参数帮助 openwrt 安装 Router:xiaomi R4C官方固件:openwrt 23.05.5 (下图标红处)官方教程 下载 OpenWRTInvasionpython remote_command_execution_vulnerability.py …...

leetcode-128.最长连续序列-day14
为什么我感觉上述代码时间复杂度接近O(2n), 虽然有while循环,但是前面有个if判断,能进入while循环的也不多,while循环就相当于两个for循环,但不是嵌套类型的: 变量作用域问题:...

梳理你的思路(从OOP到架构设计)_简介设计模式
目录 1、 模式(Pattern) 是较大的结构编辑 2、 结构形式愈大 通用性愈小编辑 3、 从EIT造形 组合出设计模式 1、 模式(Pattern) 是较大的结构 组合与创新 達芬奇說:簡單是複雜的終極形式 (Simplicity is the ultimate form of sophistication) —Leonardo d…...

JAVA前端开发中type=“danger“和 type=“text“的区别
在前端开发中,type 属性通常用于指定按钮或其他元素的样式或行为。不同的框架和库可能对 type 属性有不同的定义和用法。常见的框架包括 Bootstrap、Ant Design(antd)、Element Plus 等。下面我将分别介绍在这些框架中 type"danger"…...

python 中执行from elasticsearch import Elasticsearch,AsyncElasticsearch 报错
在 Python 中执行 from elasticsearch import Elasticsearch, AsyncElasticsearch 时,如果提示 AsyncElasticsearch 不存在,可能是因为以下几个原因: 1. 安装的 elasticsearch 库版本不匹配 AsyncElasticsearch 是在 elasticsearch 库的较新版本中引入的。如果你安装的版本…...

带有 Elasticsearch 和 Langchain 的 Agentic RAG
作者:来自 Elastic Han Xiang Choong 讨论并实现 Elastic RAG 的代理流程,其中 LLM 选择调用 Elastic KB。 更多阅读:Elasticsearch:基于 Langchain 的 Elasticsearch Agent 对文档的搜索。 简介 代理是将 LLM 应用于实际用例的…...

Jenkins持续集成部署——jenkins安装
前言 Jenkins 是一个开源的自动化服务器,主要用于持续集成(CI)和持续交付(CD)。它为软件开发团队提供了一个易于使用的平台来自动化构建、测试和部署应用程序的过程。 Jenkins 主要功能 1. 持续集成 (CI) 自动构建…...

微信小程序开发入门
实现滚动 需要设置高度和边框 轮播图 差值表达式( {{表达式的值}} ),info数据要写到js文件的data数据中 小程序中常用的事件...

深度学习中自适应学习率调度器
传统观点认为,太大的学习率不利于优化深度神经网络,而相比固定的学习率而言,变化的学习率更能提供快速的收敛。基于此,本文作者基于理论基础提出了一个计算深度神经网络学习率的新方法。实验结果证明了该方法的有效性。 训练神经…...

Phono3py hdf5文件数据读取与处理
Phono3py是一个主要用python写的声子-声子相互作用相关性质的模拟包,可以基于有限位移算法实现三阶力常数和晶格热导率的计算过程,同时输出包括声速,格林奈森常数,声子寿命和累积晶格热导率等参量。 相关介绍和安装请参考往期推荐…...

React 底部加载组件(基于antd)
底部加载组件的意义在于提供一种流畅的用户体验,以便在用户滚动到页面底部时自动加载更多内容。这样可以让用户无需离开当前页面,就能够无缝地浏览更多的内容.通过底部加载组件,可以分批加载页面内容,减少一次性加载大量数据对页面…...

将HTML转换为PDF:使用Spire.Doc的详细指南(一) 试用版
目录 引言 1. 为什么选择 Spire.Doc? 1.1 主要特点 1.2 适用场景 2. 准备工作 2.1 引入 Spire.Doc 依赖 2.2 禁用 SSL 证书验证 3. 实现功能 3.1 主类结构 3.2 代码解析 4. 处理图像 5. 性能优化 5.1 异步下载图像 示例代码 5.2 批量处理优化 示例代…...

数据结构经典算法总复习(下卷)
第五章:树和二叉树 先序遍历二叉树的非递归算法。 void PreOrderTraverse(BiTree T, void (*Visit)(TElemType)) {//表示用于查找的函数的指针Stack S; BiTree p T;InitStack(S);//S模拟工作栈while (p || !StackEmpty(S)) {//S为空且下一个结点为空,意味着结束遍…...

mac 安装graalvm
Download GraalVM 上面链接选择jdk的版本 以及系统的环境下载graalvm的tar包 解压tar包 tar -xzf graalvm-jdk-<version>_macos-<architecture>.tar.gz 移入java的文件夹目录 sudo mv graalvm-jdk-<version> /Library/Java/JavaVirtualMachines 设置环境变…...