观察者模式和订阅模式
观察者模式和订阅模式在概念上是相似的,它们都涉及到一个对象(通常称为“主题”或“发布者”)和多个依赖对象(称为“观察者”或“订阅者”)之间的关系。然而,尽管它们有相似之处,但在某些方面也存在细微的差别。
观察者模式(Observer Pattern)
核心思想
定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
结构
通常包括主题(Subject)和观察者(Observer)两个主要角色。主题维护一个观察者列表,当状态变化时,通知列表中的所有观察者。
实现方式
观察者模式可以通过在主题中维护一个观察者列表,并提供注册(addObserver)和注销(removeObserver)观察者的方法来实现。当主题状态变化时,遍历观察者列表并调用每个观察者的更新方法。
应用场景
常用于事件处理系统、GUI工具包中的事件监听器、订阅-发布系统等。
Demo
设计一个天气预报系统,当天气变化时,通知多个订阅了天气预报的用户。
定义 Subject 接口
Subject接口,被观察者,代表被观察的对象,定义注册新观察者,移除观察者,和通知观察者三个接口。
package org.example.observer;import java.util.Observer;public interface MySubject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}
实现 Subject 接口
储存观察者列表,被观察对象变化信息,通知观察者列表
package org.example.observer;import java.util.ArrayList;
import java.util.List;public class WeatherStation implements MySubject {private List<MyObserver> observers;private String weather;public WeatherStation() {observers = new ArrayList<MyObserver>();}@Overridepublic void registerObserver(MyObserver o) {this.observers.add(o);}@Overridepublic void removeObserver(MyObserver o) {this.observers.remove(o);}@Overridepublic void notifyObservers() {this.observers.forEach(observer -> observer.update(weather));}public void setWeather(String weather) {this.weather = weather;notifyObservers();}
}
定义 Observer 接口
Observer接口,代表观察者,定义接收到通知后需要执行的动作接口
package org.example.observer;public interface MyObserver {void update(String weather);
}
实现 Observer 接口
观察者接收到通知后,实现具体要执行的动作
package org.example.observer;public class User implements MyObserver {private String name;public User(String name) {this.name = name;}@Overridepublic void update(String weather) {System.out.println(String.format("name %s receive weather update : %s", name, weather));}
}
测试
package org.example.observer;public class MyObserverMain {public static void main(String[] args) {// 创建被观察者对象WeatherStation station = new WeatherStation();// 创建观察者对象User userA = new User("A");User userB = new User("B");User userC = new User("C");// 注册观察者,并更新天气station.registerObserver(userA);station.registerObserver(userB);station.registerObserver(userC);station.setWeather("Sunny");// 移除部分观察者,再次更新天气station.removeObserver(userA);station.setWeather("Rainy");}
}

订阅模式(Subscription Pattern)
核心思想
也是一种一对多的关系,但更强调“订阅”的概念。订阅者订阅某个主题或频道,以接收该主题或频道发布的更新或消息。
结构
通常包括发布者(Publisher)、订阅者(Subscriber)和消息(Message)三个主要角色。订阅者通过订阅某个发布者来接收其发布的消息。
实现方式
订阅模式可以通过事件总线(Event Bus)、消息队列(Message Queue)或专门的订阅系统来实现。订阅者可以订阅特定的主题或频道,并接收该主题或频道发布的消息。
应用场景
广泛用于消息传递系统、事件驱动架构、分布式系统中的服务间通信等。
Demo
定义消息类
package org.example.publish_subscriber;public class Message {private String content;public Message(String content) {this.content = content;}public String getContent() {return content;}
}
定义订阅者接口
package org.example.publish_subscriber;public interface Subscriber {void receive(String message);
}
实现订阅者接口
package org.example.publish_subscriber;public class User implements Subscriber{private String name;public User(String name) {this.name = name;}@Overridepublic void receive(String message) {System.out.println(String.format("%s received message: %s", name, message));}
}
定义事件总线
package org.example.publish_subscriber;import java.util.Map;
import java.util.concurrent.*;public class EventBus {private final Map<Subscriber, BlockingQueue<Message>> subscriberQueues = new ConcurrentHashMap<>();private final ExecutorService executor = Executors.newCachedThreadPool();private volatile boolean running = true;public void subscriber(Subscriber subscriber) {BlockingQueue<Message> queue = new LinkedBlockingQueue<>();subscriberQueues.put(subscriber, queue);executor.submit(() -> {try {while (running || !queue.isEmpty()) {Message message = queue.take();subscriber.receive(message);}} catch (Exception e) {Thread.currentThread().interrupt();}});}public void publish(Message message) {subscriberQueues.values().forEach(queue -> {try {queue.put(message);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}public void shutdown() {running = false;executor.shutdown();try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();}}
}
测试
package org.example.publish_subscriber;public class PubSubMain {public static void main(String[] args) throws InterruptedException {EventBus eventBus = new EventBus();User userA = new User("A");User userB = new User("B");User userC = new User("C");User userD = new User("D");eventBus.subscriber(userA);eventBus.subscriber(userB);eventBus.subscriber(userC);eventBus.subscriber(userD);for (int i = 0; i < 10; i++) {eventBus.publish(new Message(i + "Hello World!"));eventBus.publish(new Message(i + "Hello Shore!"));}Thread.sleep(10000);eventBus.shutdown();}
}

在上面的例子中,executor.submit 被用于提交订阅者线程的任务。每个订阅者都有一个对应的工作队列,当发布消息时,消息会被放入每个订阅者的工作队列中。订阅者线程会不断地从自己的工作队列中取出消息并处理。通过这种方式,实现了发布-订阅模式中的异步通信和消息分发
区别与联系
区别
观察者模式更侧重于对象间的依赖关系和状态变化的通知机制;而订阅模式更强调消息的传递和订阅-发布的关系。此外,观察者模式通常是在单个应用或系统内使用;而订阅模式可能涉及跨系统或跨网络的消息传递。
联系
两者都涉及到一个对象(主题/发布者)和多个依赖对象(观察者/订阅者)之间的关系,且都实现了某种形式的通知机制。在某些情况下,它们可以相互替代或结合使用。例如,在一个分布式系统中,可以使用订阅模式来实现不同服务之间的消息传递,而在服务内部则可以使用观察者模式来实现状态变化的通知和更新。
相关文章:
观察者模式和订阅模式
观察者模式和订阅模式在概念上是相似的,它们都涉及到一个对象(通常称为“主题”或“发布者”)和多个依赖对象(称为“观察者”或“订阅者”)之间的关系。然而,尽管它们有相似之处,但在某些方面也…...
基于ToLua的C#和Lua内存共享方案保姆级教程
C#和Lua内存共享方案保姆级教程 前言 在介绍C#和Lua内存共享方案之前,先介绍下面两个点来支撑这个方案的必要性 跨语言交互很费 Lua和C#交互最早是基于反射的方式实现的,后来为了提升性能发展成Luajit+C#静态方法导出注入到lua虚拟机的方式至此Lua+Unity的性能才达到了实…...
OpenCV与AI深度学习|16个含源码和数据集的计算机视觉实战项目(建议收藏!)
本文来源公众号“OpenCV与AI深度学习”,仅用于学术分享,侵权删,干货满满。 原文链接:分享|16个含源码和数据集的计算机视觉实战项目 本文将分享16个含源码和数据集的计算机视觉实战项目。具体包括: 1. 人…...
Vue 如何简单更快的对 TypeScript 中接口的理解?应用场景?
TypeScript 中接口(Interface)的理解与应用 在 TypeScript 中,接口(Interface) 是一种用来定义对象的结构或形状的方式。接口可以指定对象中应该包含哪些属性、这些属性的类型以及它们的函数签名。接口帮助我们在代码…...
R语言绘图过程中遇到图例的图块中出现字符“a“的解决方法
R语言绘图过程中遇到图例的图块中出现字符的解决方法 因为我遇到这个问题的时候没在网上找到合适的方法,找到个需要付费的,算了。也许是因为问的方式不同,问了半天AI也回答出来,莫名有些烦躁,打算对代码做个分析&…...
视图合并机制解析 | OceanBase查询优化
背景 在默认配置下,若查询语句中嵌入了视图,系统会先等待视图内部所包含的查询完全执行完成后,再继续执行父查询。这种方式造成优化器无法将视图查询与外层查询视为一个整体来进行优化处理,从而限制了优化效果。因此,…...
sql注入报错分享(mssql+mysql)
mysql mysql的报错内容比较多 网上也有比较多的 这里重复的就不多介绍了。一笔带过 溢出类 bigint 当超过mysql的整形的时候,就会导致溢出,mysql可能会将错误信息带出。这里user()是字母默认为0 取反以后1可能就会导致异常。 报错特征 BIGINT UNSIG…...
PHP 高并发解决方案
PHP作为一种脚本语言,在处理高并发请求时可能面临一些挑战。但通过合理的设计和优化,可以有效提升PHP应用程序的性能和并发处理的能力。 一、缓存 页面缓存:将生成的页面缓存起来,减少对数据库的查询,提高响应速度。…...
k8s1.30.0高可用集群部署
负载均衡 nginx负载均衡 两台nginx负载均衡 vim /etc/nginx/nginx.conf stream {upstream kube-apiserver {server 192.168.0.11:6443 max_fails3 fail_timeout30s;#server 192.168.0.12:6443 max_fails3 fail_timeout30s;#server 192.168.0.13:6443 max_fails3…...
多摩川编码器协议及单片机使用
参考: https://blog.csdn.net/qq_28149763/article/details/132718177 https://mp.weixin.qq.com/s/H4XoR1LZSMH6AxsjZuOw6g 1、多摩川编码器协议 多摩川数据通讯是基于485 硬件接口标准NRZ 协议,通讯波特率为2.5Mbps 的串行通讯,采用差分两…...
Android 网络通信(三)OkHttp实现登入
学习笔记 目录 一. 先写XML布局 二、创建 LoginResponse 类 :封装响应数据 目的和作用: 三、创建 MyOkHttp 类 :发送异步请求 代码分析 可能改进的地方 总结 四、LoginActivity 类中实现登录功能 详细分析与注释: 总结: 改进建议: 零、响应数据样例 通过 P…...
分享一下arr的意义(c基础)(必看)(牢记)
arr 即数组名 一般指数组首元素地址 在两种情况下不是 1:sizeof(arr) arr指整个数组简单讲解一下strlen与sizeof(c基础)_strzeof在c语言中什么意思-CSDN博客 2:printf("%p",&…...
AGENT AI 综述核心速览
研究背景 研究问题:这篇文章探讨了多模态人工智能(Agent AI)系统在理解和响应视觉和语言输入方面的潜力,特别是在物理和虚拟环境中的应用。Agent AI旨在通过感知和行动来增强人工智能系统的交互性和适应性。研究难点:…...
基于Java Springboot房屋租赁系统
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…...
力扣 LeetCode 701. 二叉搜索树中的插入操作(Day10:二叉树)
解题思路: 全部插入到叶子节点即可 class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {if (root null) {TreeNode node new TreeNode(val);return node;}if (root.val < val) {root.right insertIntoBST(root.right, val);}if (root…...
猎板科技:PCB 特殊定制领域的卓越引领者
一、专业团队,创新设计之源 猎板科技的核心竞争力首先源于其卓越的专业团队。这支队伍汇聚了经验丰富的资深工程师以及行业前沿的技术专家,他们在 PCB 设计领域拥有深厚的造诣和敏锐的洞察力。无论是面对常规 PCB 设计任务,还是应对极具挑战…...
centos stream 9安装docker教程
第一步:安装该dnf-plugins-core软件包(它提供了管理 DNF 存储库的命令) sudo dnf -y install dnf-plugins-core 第二步:设置存储库(这里使用的是阿里云的镜像源) sudo dnf config-manager --add-repo https://mirrors.aliyun.c…...
优化旧LabVIEW程序功能的方法
优化运行已久的LabVIEW程序时,需在不影响原有功能的基础上针对目标功能进行改进。以下结合一个数据采集功能优化的实例,详细说明操作步骤和注意事项,为工程师提供切实可行的方法。 优化背景 某企业的LabVIEW程序负责多通道数据采集…...
关于安卓模拟器或手机设置了BurpSuite代理和安装证书后仍然抓取不到APP数据包的解决办法
免责申明 本文仅是用于学习研究安卓系统设置代理后抓取不到App数据包实验,请勿用在非法途径上,若将其用于非法目的,所造成的一切后果由您自行承担,产生的一切风险和后果与笔者无关;本文开始前请认真详细学习《中华人民共和国网络安全法》【学法时习之丨网络安全在身边一…...
【电路笔记】-布尔逻辑AND函数
逻辑AND函数 文章目录 逻辑AND函数1、概述2、逻辑 AND 函数 仅当所有输入均为 true 时,逻辑与函数输出才为 true,否则输出为 false。 1、概述 布尔代数基于逻辑函数,其中每个布尔函数(例如逻辑 AND 函数)通常具有一个…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
