观察者模式:对象之间的订阅机制
欢迎来到设计模式系列的第十三篇文章!在之前的文章中,我们学习了许多常用的设计模式,今天我们将介绍观察者模式,它是一种行为型设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
在学习观察者模式前,我们可以带着一下三个问题来学习:
1.谁是观察者,谁又是被观察者?
2.观察者如何”观察”被观察者的?
3.为什么要使用观察着模式?
4.你在工作中见过哪些观察者模式?
观察者模式简介
观察者模式是一种常用的设计模式,它用于构建对象之间的发布-订阅(Publish-Subscribe)关系。在观察者模式中,有两类核心角色:
- 主题(Subject):主题是被观察的对象,它维护了一个观察者列表,可以动态地添加或删除观察者。主题通常具有一种状态,当状态发生变化时,会通知所有观察者。
- 观察者(Observer):观察者是依赖于主题的对象,它们会注册到主题上,以便在主题的状态发生变化时得到通知并执行相应的操作。
观察者模式的核心思想是降低主题和观察者之间的耦合度,使得它们可以独立地变化。这种松耦合的设计可以更好地支持可维护性和可扩展性。
为什么需要观察者模式?
在软件开发中,经常会遇到一对多的场景,例如:
- 一个新闻网站需要通知多个订阅者(用户)有新文章发布。
- 一个股票市场应用需要通知多个投资者股票价格的变化。
- 一个气象站需要通知多个应用天气信息的变化。
如果没有观察者模式,我们可能会采用硬编码的方式来实现这些通知,但这样会导致高耦合和不易维护的问题。观察者模式通过将主题和观察者分离,使得它们可以独立变化,从而更好地应对这类场景。
观察者模式的实现
观察者模式的实现通常包括以下几个关键元素:
- 主题接口(Subject):定义了主题对象的基本操作,包括注册观察者、删除观察者和通知观察者等。
- 具体主题(ConcreteSubject):实现了主题接口,并维护了一个观察者列表。具体主题通常具有一个状态,当状态发生变化时,会通知所有注册的观察者。
- 观察者接口(Observer):定义了观察者对象的更新操作,通常包括一个
update
方法。 - 具体观察者(ConcreteObserver):实现了观察者接口,并注册到具体主题上。当主题状态发生变化时,具体观察者的
update
方法会被调用。
现在,让我们通过一个示例来演示观察者模式的实现。假设我们正在开发一个简单的股票市场应用,股票价格会不断变化,我们需要通知多个投资者股票价格的变化情况。
首先,我们定义观察者接口 Observer
:
public interface Observer {void update(double price);
}
然后,我们定义主题接口 Subject
:
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
接下来,我们创建一个具体主题 StockMarket
,它继承了 Subject
接口:
import java.util.ArrayList;
import java.util.List;public class StockMarket implements Subject {private List<Observer> observers = new ArrayList<>();private double price;@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(price);}}public void setPrice(double price) {this.price = price;notifyObservers();}
}
在 StockMarket
类中,我们维护了一个观察者列表 observers
和股票价格 price
。当 setPrice
方法被调用时,会通知所有注册的观察者。
接下来,我们创建一个具体观察者 Investor
,它实现了 Observer
接口:
public class Investor implements Observer {private String name;public Investor(String name) {this.name = name;}@Overridepublic void update(double price) {System.out.println(name + " 收到股票价格更新,当前价格为 " + price);}
}
最后,我们可以测试观察者模式的效果:
public class Main {public static void main(String[] args) {StockMarket stockMarket = new StockMarket();Investor investor1 = new Investor("Alice");Investor investor2 = new Investor("Bob");stockMarket.registerObserver(investor1);stockMarket.registerObserver(investor2);stockMarket.setPrice(100.0);stockMarket.setPrice(105.0);stockMarket.removeObserver(investor1);stockMarket.setPrice(110.0);}
}
以上代码创建了一个股票市场 StockMarket
和两个投资者 Investor
,并演示了股票价格的变化如何通知投资者。
观察者模式的优点
观察者模式具有许多优点,使其成为软件开发中常用的设计模式之一:
- 降低耦合度:观察者模式将主题和观察者分离,主题不需要知道观察者的具体细节,从而降低了它们之间的耦合度。
- 支持广播通信:主题状态变化时,可以通知多个观察者,实现了一对多的通信,方便信息广播。
- 开闭原则:通过增加新的观察者类和主题类,可以扩展观察者模式,符合开闭原则。
- 可维护性:因为观察者和主题之间的关系是松散的,所以更容易维护和修改。
观察者模式的缺点
观察者模式也存在一些缺点,需要考虑:
- 观察者太多时性能问题:如果观察者太多,通知所有观察者可能会影响性能,尤其是在大规模系统中。
- 顺序问题:观察者的通知顺序可能不确定,如果有顺序要求,需要额外处理。
- 可能导致循环依赖:主题和观察者之间的循环依赖可能引入问题,需要小心处理。
观察者模式的应用场景
观察者模式适用于以下场景:
- 一对多的依赖关系:当一个对象的状态变化需要通知多个其他对象时,观察者模式非常适用。例如,新闻发布、股票市场更新等。
- 抽象模型与实现分离:当需要将抽象模型与其具体实现分离时,观察者模式可以帮助实现这种分离。例如,图形界面框架中的事件处理。
- 动态系统:在动态系统中,对象的数量和类型可能会随时改变,观察者模式允许动态地添加或删除观察者。
观察者模式的实际应用
观察者模式在现实世界和软件开发中都有广泛应用。以下是一些实际应用示例:
- 邮件订阅:邮件订阅服务是观察者模式的一个典型应用。用户可以订阅不同类型的邮件通知,当有新邮件到达时,订阅者会收到通知。
- 社交媒体通知:社交媒体平台可以通知用户关注的人或页面的更新,例如,新的帖子、消息或评论。
- 股票市场应用:股票市场应用通常使用观察者模式来实时通知投资者股票价格的变化。
- 事件处理:图形用户界面(GUI)框架使用观察者模式来处理用户事件,例如,鼠标点击、键盘输入等。
最佳实践
在使用观察者模式时,有一些最佳实践值得注意:
- 避免循环依赖:确保主题和观察者之间没有循环依赖,以防止潜在的问题。
- 考虑多线程情况:如果在多线程环境中使用观察者模式,确保实现线程安全的方式来处理观察者列表和状态更新。
- 谨慎使用广播通知:通知所有观察者可能会影响性能,如果只有部分观察者关心状态变化,可以考虑使用条件通知。
想进一步了解观察者模式的老铁可以了解一下 spring中的事件机制:深入理解事件发布监听机制
总结
观察者模式是一种非常有用的设计模式,用于实现对象之间的松耦合通信。通过定义一对多的依赖关系,主题状态变化时通知多个观察者,实现了对象之间的订阅机制。在实际应用中,观察者模式可以帮助我们构建灵活、可扩展的系统。
相关文章:
观察者模式:对象之间的订阅机制
欢迎来到设计模式系列的第十三篇文章!在之前的文章中,我们学习了许多常用的设计模式,今天我们将介绍观察者模式,它是一种行为型设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时&…...

【1462. 课程表 IV】
来源:力扣(LeetCode) 描述: 你总共需要上 numCourses 门课,课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite ,其中 prerequisites[i] [ai, bi] 表示如果你想选 bi 课程,你…...

Kerberos 身份验证
简介 Kerberos 是一种由 MIT(麻省理工大学)提出的一种基于加密 Ticket 的身份认证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证,用于验证用户或主机的标识。。 适用范围:Windows Server 2022、Window…...

R语言贝叶斯METROPOLIS-HASTINGS GIBBS 吉布斯采样器估计变点指数分布分析泊松过程车站等待时间...
原文链接:http://tecdat.cn/?p26578 指数分布是泊松过程中事件之间时间的概率分布,因此它用于预测到下一个事件的等待时间,例如,您需要在公共汽车站等待的时间,直到下一班车到了(点击文末“阅读原文”获取…...

通付盾入选2023年度“上市苗圃工程”重点企业
近日,2023年度苏州工业园区企业上市苗圃工程认定名单公示,江苏通付盾科技有限公司成功入选园区“上市苗圃工程”重点企业。 2023年第一批次苗圃企业认定结果: 企业上市苗圃工程 上市企业是衡量地方综合经济实力的重要标尺,也是区…...
SpringMVC之文件上传下载
SpringMVC是一个基于Java的Web框架,它提供了一套用于构建Web应用程序的开发模型。在SpringMVC中,文件上传和下载是常见的功能之一。 SpringMVC文件上传和下载的介绍: 介绍文件上传: 在SpringMVC中,文件上传功能可以通…...

嵌入式IDE(2):KEIL中SCF分散加载链接文件详解和实例分析
在上一篇文章IAR中ICF链接文件详解和实例分析中,我通过I.MX RT1170的SDK中的内存映射关系,分析了IAR中的ICF链接文件的语法。对于MCU编程所使用的IDE来说,IAR和Keil用得比较多,所以这一篇文章就来分析一下Keil的分散文件.scf(scat…...
Linux防火墙常用操作及端口开放
Linux防火墙常用操作及端口开放 1.查看防火墙状态 firewall-cmd --state 2.开启防火墙 systemctl start firewalld.service 3.开启指定端口 firewall-cmd --zonepublic --add-port3306/tcp --permanent firewall-cmd --zonepublic --add-port6379/tcp --permanent 显示success表…...
[JAVAee]Linux上的javax.mail报错
我们把在window写的项目部署到Linux上的Tomcat时,如果发现使用不了了,该如何找到错误呢?找到报错的地方在哪呢? 在Linux环境下来到Tomcat目录下的logs目录,输入: tail -f catalina.out -n 500 tail 就是把文件的末尾几行读取到终端上,并会持续刷新 -f 循环读取 catalina.ou…...

开学季|校园迎新哪家强?VR全景来导航
九月开学迎新季,各大高校的迎新活动开展的如火如荼,随着科技的不断进步,高校为了更好的开展迎新活动,让新生们尽快熟悉新的校园和生活,会利用VR全景技术带领着新生进行校园游览,给予新生们巨大便利的同时&a…...

el-checkbox-group限制勾选数量
<!--* Description: 视频监控 页面* Author: mhf* Date: 2023-08-15 13:26:33 --> <template><div class"videoSurveillance"><el-row :gutter"24"><el-col :span"4"><div class"videoSurveillance-left&…...

【JavaScript】WebAPI入门到实战
文章目录 一、WebAPI背景知识1. 什么是WebAPI?2. 什么是API? 二、DOM基本概念三、获取元素三、事件初识1. 点击事件2. 键盘事件 四、操作元素1. 获取/修改元素内容2. 获取/修改元素属性3. 获取/修改表单元素属性4. 获取/修改样式属性 五、操作节点1. 新增…...

奥康的高尔夫鞋,圈不住投资者的心
文 | 螳螂观察 作者 | 青月 鞋服行业终于熬过了“寒冬”,2023年行业景气度开始逐步回暖。 东方财富Choice数据显示,截至8月17日,已有28家鞋帽服装类上市公司发布了2023年中期业绩预告或快报,其中,9家预增࿰…...

vue2配置环境变量并且nginx运行成功
需求:我在vue项目配置了生产环境和开发环境,之后通过proxy代理的方式把地址转发到真实的服务器地址上用于请求接口,之后把项目打包后上传到nginx上,之后接口报错404,但是本地运行是可以访问的,找了很久终于…...
Java+Swing形成GUI图像界面
一、Swing 简介 Swing 主要用来开发 GUI 程序,GUI(Graphical User Interface)即图形用户界面。Java 中针对 GUI 设计提供了丰富的类库,这些类分别位于 java.awt 和 java.swing 中,简称 AWT 和 Swing ;其中,AWT(Abstract Window Toolkit)是抽象窗口工具包,是 Java 平…...
编辑距离 -- 动规
72. 编辑距离 给出动规的两种常见实现形式:自顶向下、自底向上,前者一般借助递归函数备忘录实现,后者通常基于dp数组实现。 class MinDistance:"""72. 编辑距离https://leetcode.cn/problems/edit-distance/""&quo…...
douyin【商品抢购js脚本】
文章目录 前言订阅须知知识点源码前言 脚本主要用来实现抢购douyin商城、直播间秒杀商品等一系列商品 订阅须知 订阅后,只提供js源代码,不提供教学,请根据源码自行抓包知识点 1、在查询串插入一个固定的键rstr 2、对查询串进行按键排序并取值,对空格和+进行转义为a …...

常见Web安全技术总结!474页Web安全从入门到精通(附PDF)
Web安全范围比较大,知识点比较杂,很多朋友都无从下手,这不可怕,可怕的是乱下手,其实往往基础才是决定你是否能走远的关键。 为了帮助大家入门网安,给大家推荐一份《新手Web安全入门到精通》,共…...

Prometheus 监控指南:如何可靠地记录数字时间序列数据
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: 📚…...

rsync远程同步+inotify监控
目录 一、Rsync 简介 1、rsync是什么 2、备份的方式 3、rsync同步方式 4、常用rsync命令 5、配置源的两种表达方法 二、rsync实验 1、本地复制 编辑编辑 2、异地复制 2.1 rsync服务器配置 2.2 rsync客户端配置 2.2.1 普通同步 2.2.2 免密同步 2.2.3 --delet…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...