当前位置: 首页 > news >正文

观察者模式学习

观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。在 GoF 的《设计模式》一书中,它的定义是这样的:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
中文翻译:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式。

从定义中可以知道,观察者主要应用于通知的场景,并且通知的对象可能需要频繁的改变。它可以解耦被观察者与观察者。
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Feeds,本质上都是观察者模式。

经典实现方式
我们先来看其中最经典的一种实现方式。这也是在讲到这种模式的时候,很多书籍或资料给出的最常见的实现方式。具体的代码如下所示:

public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers(Message message);
}public interface Observer {void update(Message message);
}public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<Observer>();@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(Message message) {for (Observer observer : observers) {observer.update(message);}}}public class ConcreteObserverOne implements Observer {@Overridepublic void update(Message message) {//TODO: 获取消息通知,执行自己的逻辑...System.out.println("ConcreteObserverOne is notified.");}
}public class ConcreteObserverTwo implements Observer {@Overridepublic void update(Message message) {//TODO: 获取消息通知,执行自己的逻辑...System.out.println("ConcreteObserverTwo is notified.");}
}public class Demo {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();subject.registerObserver(new ConcreteObserverOne());subject.registerObserver(new ConcreteObserverTwo());subject.notifyObservers(new Message());}
}

这个例子很好理解:

它具有一个被观察者接口(Subject)和一个观察者接口(Observer);
所有具体的被观察者都需要实现Subject接口,即所有的被观察者都具有注册观察者方法(registerObserver)、删除观察者方法(removeObserver)、通知观察者方法(notifyObservers);
所有具体的观察者都需要实现Observer接口,即所有的观察者都具有执行通知逻辑的方法(update);
在被观察者中通过一个List来保存所有注册的观察者,在需要时通过遍历List来通知所有的观察者。

但上面的代码算是观察者模式的“模板代码”,只能反映大体的设计思路。在真实的软件开发中,并不需要照搬上面的模板代码。而且Java中也有一些第三方框架供我们使用,它们提供了实现观察者模式的骨架代码,我们可以基于此框架,非常容易地在自己的业务场景中实现观察者模式,不需要从零开始开发,如Guava 的EventBus、Spring Event等。

观察者模式的实现原理分析

如何通知观察者?
我们看到,被观察者需要通知每一个观察者,所以上面示例中被观察者通过一个List来保存了观察者,然后通过遍历List来实现通知每一个观察者。当然通过其他的容器也可以实现,比如Guava的EventBus就是用ConcurrentMap来存储的观察者信息的。
还有被观察者中保存的是观察者的接口,并不知道观察者的细节,即被观察者和观察者之间用松耦合方式结合(loosecoupling),符合“为交互对象之间的松耦合设计而努力”的设计原则。
在上面示例中是被观察者主动将消息推送到观察者中的,这种方式被称为推(push)。还有一种称为拉(pull)的方式,即被观察者通知观察者后,观察者再从被观察者中获取所需的数据。pull方式的前提条件是,被观察者需要提供对外获取数据的接口,同时观察者中需要能获取到被观察者对象。显然,pull方式使被观察者与观察者联系更紧密了,这不一定是一个好的现象

如何更具有复用性?
被观察者需要提供注册、通知等通用接口,其实这些操作和我们实际业务关系不大,可以考虑抽取出来。此时有两种可以考虑的方式,
一种是继承方式,将通用的操作都抽取到父类中,所有的被观察者只需要继承父类就可以具有注册、通知等操作的实现,JDK1.0中的观察者模式就是这样做的,它提供了Observable类用于给被观察者继承。
一种是组合方式,将通用的操作都抽取到其他类中,被观察者通过委托该类来实现注册、通知等通用操作。如果使用Guava的EventBus,就是通过组合EventBus类来实现的。
我们都知道组合的方式优于继承,因为继承有诸多限制,比如单继承。所以一般都推荐使用组合的方式,目前常用的第三方框架采用的都是组合的方式。

同步通知与异步通知
在通知观察者的方式上我们可以有两种方式,同步阻塞的实现方式和异步非阻塞的实现方式。
同步阻塞的实现方式
观察者和被观察者代码在同一个线程内执行,被观察者一直阻塞,直到所有的观察者代码都执行完成之后,才执行后续的代码。
上面讲到的用户注册的例子就是同步阻塞方式,register() 函数依次调用执行每个观察者的 handleRegSuccess() 函数,等到都执行完成之后,才会返回结果给客户端。
异步非阻塞的实现方式
如果注册接口是一个调用比较频繁的接口,对性能非常敏感,希望接口的响应时间尽可能短,那我们可以将同步阻塞的实现方式改为异步非阻塞的实现方式,以此来减少响应时间。
实现异步非阻塞的方式我们既可以自己手动创建线程来实现,也可以采用第三方框架提供的异步执行功能,如如Guava 的EventBus、Spring Event等。
跨进程间的调用
我们一般所介绍的观察者模式都是指在一个进程间(通常就是指同一个项目代码)进行的通知,但其实观察者模式也可以应用于进程间,比如如果用户注册成功之后,我们需要发送用户信息给大数据征信系统,而大数据征信系统是一个独立的系统,跟它之间的交互是跨不同进程的。
要实现跨进程的观察者模式,其实就是要实现进程间的通讯,这个我们就比较熟悉了,常用的就是:

RPC 接口调用的方式。
消息队列的方式。(采用消息队列的发布-订阅模式)

消息队列方式相对于RPC方式来说,被观察者和观察者解耦更加彻底,两部分的耦合更小,因为被观察者完全不感知观察者,同理,观察者也完全不感知被观察者。不过弊端就是需要引入一个新的系统(消息队列),增加了维护成本。

相关文章:

观察者模式学习

观察者模式&#xff08;Observer Design Pattern&#xff09;也被称为发布订阅模式&#xff08;Publish-Subscribe Design Pattern&#xff09;。在 GoF 的《设计模式》一书中&#xff0c;它的定义是这样的&#xff1a; Define a one-to-many dependency between objects so th…...

人工智能_机器学习078_聚类算法_概念介绍_聚类升维_降维_各类聚类算法_有监督机器学习_无监督机器学习---人工智能工作笔记0118

首先看一下什么是聚类,我们可以进入sklearn的官网去看看 可以看到这里,首先classification 这个分类我们学完了,然后就是regression回归我们也学完了对吧,其实我们现实生活中的,大部分问题就是 这两种问题就可以解决了. 然后我们再来看一个: clustering,这个就是聚类对吧.聚类算…...

基于AR+地图导航的景区智慧导览设计

随着科技的飞速发展&#xff0c;智慧旅游已经成为现代旅游业的一个重要趋势。在这个背景下&#xff0c;景区智慧导览作为智慧旅游的核心组成部分&#xff0c;正逐渐受到越来越多游客的青睐。本文将深入探讨地图导航软件在景区智慧导览中的应用&#xff0c;并分析其为游客和景区…...

git基本指令

下载代码 git clone http://.......设置分支 git checkout 分支名查询当前分支 git checkout打开终端或命令行窗口&#xff0c;进入你要操作的项目目录&#xff0c;执行以下命令&#xff0c;列出所有的分支&#xff0c;这会列出当前代码仓库中的所有分支&#xff0c;用带星号…...

ECMAScript基础入门

ECMAScript&#xff08;简称ES&#xff09;是一种标准化了的高级编程语言&#xff0c;它是JavaScript语言的标准化版本&#xff0c;由Ecma International组织发布。ECMAScript描述了JavaScript的语法和核心特性&#xff0c;而JavaScript是实现ECMAScript标准的编程语言。随着We…...

神经网络介绍

目录 知识点介绍 知识点介绍 前馈神经网络&#xff1a;&#xff08;前馈网络的数据只向一个方向传播&#xff09; RNN循环神经网络&#xff0c;下图中多个 RNN 层都是“同一个层”&#xff0c;这一点与之前的神经网络是不一样的。...

CPU亲和性和NUMA架构

何为CPU的亲和性 CPU的亲和性&#xff0c;进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性&#xff0c;进程迁移的频率小就意味着产生的负载小。亲和性一词是从affinity翻译来的&#xff0c;实际可以称为CPU绑定。 在多核运行的机器上&#xff0c;…...

目标检测-Two Stage-Fast RCNN

文章目录 前言一、Fast RCNN的网络结构和流程二、Fast RCNN的创新点1.特征提取分类回归合一2.更快的训练策略 总结 前言 前文目标检测-Two Stage-SPP Net中提到SPP Net的主要缺点是&#xff1a; 分开训练多个模型困难且复杂尽管比RCNN快10-100倍&#xff0c;但仍然很慢SPP Ne…...

vol----随记!!!

目录 一、代码生成1.先新建一个功能的对应的代码配置各项解释&#xff1a; 2.后设置配置菜单3.再点保存&#xff0c;生成vue页面&#xff0c;生成model&#xff0c;生成业务类4.再通过菜单设置编写系统菜单 一、代码生成 1.先新建一个功能的对应的代码配置 各项解释&#xff…...

vue中样式动态绑定写法

绑定样式: class样式 写法:class"xxx"xXX可以是字符串、对象、数组。 字符串写法适用于:类名不确定&#xff0c;要动态获取。 对象写法适用于:要绑定多个样式,个数不确定&#xff0c;名字也不确定。 数组写法适用于:要绑定多个样式&#xff0c;个数确定&#xff0c;…...

C语言—每日选择题—Day63

指针相关博客 打响指针的第一枪&#xff1a;指针家族-CSDN博客 深入理解&#xff1a;指针变量的解引用 与 加法运算-CSDN博客 第一题 1. 设C语言中&#xff0c;一个int型数据在内存中占2个字节&#xff0c;则unsigned int型数据的取值范围为 A&#xff1a;0~255 B&#xff1a;0…...

Mac_通过chmod处理文件权限

chmod 简介 chmod 是一个 Unix 和类 Unix 系统中的命令&#xff0c;用于更改文件或目录的权限。chmod 的名称来源于 “change mode”&#xff0c;它允许用户修改文件或目录的读取&#xff08;read&#xff09;、写入&#xff08;write&#xff09;和执行&#xff08;execute&a…...

实战指南:使用 Spring Cloud Stream 集成 Kafka 构建高效消息驱动微服务

实战指南&#xff1a;使用 Spring Cloud Stream 集成 Kafka 构建高效消息驱动微服务 视频地址&#xff1a; Stream为什么被引入-尚硅谷SCS-1-内容介绍-图灵诸葛 官方文档&#xff1a; Spring Cloud Stream 什么是 Spring Cloud Stream? Spring Cloud Stream(SCS) 是一个用于构…...

线性代数基础【3】向量

第一节 向量的概念与运算 一、基本概念 ①向量 ②向量的模(长度) ③向量的单位化 ④向量的三则运算 ⑤向量的内积 二、向量运算的性质 (一)向量三则运算的性质 α β β αα (β γ) (α β) γk (α β) kα kβ(k l) α kα lα (二)向量内积运…...

Spring Boot + MinIO 实现文件切片极速上传技术

文章目录 1. 引言2. 文件切片上传简介3. 技术选型3.1 Spring Boot3.2 MinIO 4. 搭建Spring Boot项目5. 集成MinIO5.1 配置MinIO连接信息5.2 MinIO配置类 6. 文件切片上传实现6.1 控制器层6.2 服务层6.3 文件切片上传逻辑 7. 文件合并逻辑8. 页面展示9. 性能优化与拓展9.1 性能优…...

uniapp中如何使用image图片

当在UniApp中使用图片时&#xff0c;可以通过<image>标签将图片显示在页面上。这个标签可以指定src属性来引用图片&#xff0c;并且可以通过mode属性来设置图片的显示模式。除此之外&#xff0c;还可以利用click事件来实现图片的点击事件。在编写代码时&#xff0c;要注意…...

docker-compose 安装gitlab

写在前面的话&#xff1a;docker-compose的文件是通用的&#xff0c;因此可以切换任意版本的gitlab的镜像版本。 往期docker-compose部署系列如&#xff1a; docker-compose语法格式docker-compose部署openldapdocker-compose 安装Sonar并集成gitlab 文章目录 1. 参考文档2. 环…...

到底是前端验证还是后端验证

背景 软件应用研发中&#xff0c; 前端验证还是后端验证这是意识与认知问题。鉴于某些入门同学还不清楚&#xff0c;我们再来看下&#xff1a; 一. 从软件行业来自国外 Q: 前端验证和后端验证都是对同一个数据的验证&#xff0c;有什么区别&#xff1f; A: 二者的目的不同&…...

AlignBench:量身打造的中文大语言模型对齐评测

对齐&#xff08;Alignment&#xff09;&#xff0c;是指大语言模型&#xff08;LLM&#xff09;与人类意图的一致性。换言之&#xff0c;就是让LLM生成的结果更加符合人类的预期&#xff0c;包括遵循人类的指令&#xff0c;理解人类的意图&#xff0c;进而能产生有帮助的回答等…...

asp.net core 教程

asp.net core 教程 写在前面新建项目Get和PostGETPOST MVC-模型控制视图如何通俗理解MVCMVC架构---文件夹详解Connected ServicesPropertieswwwroot依赖项ControllersModelsViews 代码实例 API模型&#xff08;前后端分离&#xff09;前端代码后端代码 文件配置优先级优先级顺序…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...