【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介
观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式使用三个类Subject、Observer和Client。Subject对象带有绑定观察者到Client对象和从Client对象解绑观察者的方法。我们创建Subject类、Observer抽象类和扩展了抽象类Observer的实体类。
作用
- 一个对象状态更新,其他依赖对象收到通知和自动更新的机制。
- 实现模块化分离,实现主题与观察者交互对象之间的松耦合。
1)观察者定义了对象之间一对多的关系。
2)被观察者(主题)用一个共同的接口来更新观察者。
3)观察者和被观察者用松耦合方式结合,被观察者不知道观察者的细节,只知道观察者实现了观察者接口。
实现步骤
- 创建观察者observer基础接口,包含主题和更新方法
- 创建主题subject抽象类,包含observer列表以及添加和删除方法
- 创建具体的主题类,实现通知方法,发布通知时轮询通知全部观察者
- 创建多个具体观察者,与主题关联,并实现自己的更新方法
- 客户调用时先声明主题,再将观察者分别添加到主题,当主题发布通知时,观察者自动更新
UML

Java代码
观察者接口
// ObserverAPI.java 观察者接口,Java 9已经默认支持Observer接口 | |
// 这里避免冲突采取ObserverAPI命名 | |
public interface ObserverAPI { | |
public Subject subject = null; | |
public void update(String content); | |
} |
具体观察者
// ConcreteObserver.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
// 不同的观察者也可以对应多个主题 | |
public class ConcreteObserver implements ObserverAPI { | |
public Subject subject; | |
// 给观察者绑定主题,同时把观察者添加到主题列表 | |
public ConcreteObserver(Subject subject) { | |
this.subject = subject; | |
this.subject.register((ObserverAPI) this); | |
} | |
// 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听 | |
public void update(String content) { | |
System.out.println(String.format("%s::update() [subject.name = %s content = %s]", | |
this.getClass().getName(), | |
this.subject.getName(), content)); | |
} | |
} |
// ConcreteObserver2.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
// 不同的观察者可以对应不同的主题。 | |
public class ConcreteObserver2 implements ObserverAPI { | |
// 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者 | |
public ConcreteObserver2() { | |
} | |
// 观察者发出更新通知,观察者自行监听 | |
public void update(String content) { | |
System.out.println(String.format("%s::update() [content = %s]", | |
this.getClass().getName(), content)); | |
} | |
} |
抽象主题类
// Subject.java 定义抽象主题类或者接口,供具体主题类继承 | |
public abstract class Subject { | |
private String name; | |
// protected Set<ObserverAPI> observers = new HashSet<>(); | |
protected List<ObserverAPI> observers = new ArrayList<>(); | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public void register(ObserverAPI observer) { | |
System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]"); | |
observers.add(observer); | |
} | |
public void remove(ObserverAPI observer) { | |
observers.remove(observer); | |
} | |
// 通知由具体类来实现逻辑 | |
public abstract void notify(String name); | |
} |
具体主题类
// ConcreteSubject.java 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 | |
public class ConcreteSubject extends Subject { | |
public ConcreteSubject(String name) { | |
this.setName(name); | |
} | |
// 不同的主题类有自己的通知方法,批量通知绑定的观察者 | |
@Override | |
public void notify(String content) { | |
System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]"); | |
for (Object observer : this.observers) { | |
((ObserverAPI) observer).update(content); | |
} | |
} | |
} |
测试调用
/** | |
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者 | |
* 当发布者发布消息时,批量广播通知,而无需逐一通知 | |
* 观察者监听到消息后自己决定采取哪一种行为 | |
*/ | |
// 定义一个主题,也就是发布者 | |
Subject concreteSubject = new ConcreteSubject("subject1"); | |
// 再声明观察者,通过构造器注册到主题上 | |
ObserverAPI observer1 = new ConcreteObserver(concreteSubject); | |
// 也可以单独给主题注册一个新的观察者 | |
concreteSubject.register(new ConcreteObserver2()); | |
// 可以移除观察者对象,可以打开注释试下 | |
// concreteSubject.remove(observer1); | |
// 主题开始发布新通知,各观察者自动更新 | |
concreteSubject.notify("hello, this is broadcast."); | |
Python代码
观察者接口
# ObserverAPI.py 观察者抽象父类,定义一些公共方法 | |
class ObserverAPI: | |
def __init__(self, name): | |
self.name = name | |
# 观察者发出更新通知,观察者自行监听 | |
def update(self, content): | |
print(self.__class__.__name__ + '::update() [content = ' + content + ']') | |
def set_name(self, name): | |
self.name = name |
具体观察者
# ConcreteObserver.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
# 不同的观察者也可以对应多个主题 | |
from src.ObserverAPI import ObserverAPI | |
# 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
# 不同的观察者也可以对应多个主题 | |
class ConcreteObserver(ObserverAPI): | |
# 给观察者绑定主题,同时把观察者添加到主题列表 | |
def __init__(self, subject, name): | |
ObserverAPI.__init__(self, name) | |
# python3支持的父类调用 | |
# super(ConcreteObserver, self).__init__(name) | |
# super().__init__(name) | |
self.subject = subject | |
subject.register(self) | |
# 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听 | |
def update(self, content): | |
print(self.__class__.__name__ + '::update() [subject.name = ' + | |
self.subject.name + ' content = ' + content + ']') |
# ConcreteObserver2.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
# 不同的观察者可以对应不同的主题。 | |
from src.ObserverAPI import ObserverAPI | |
# 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 | |
# 不同的观察者可以对应不同的主题。 | |
class ConcreteObserver2(ObserverAPI): | |
# 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者 | |
# 观察者发出更新通知,观察者自行监听 | |
# def update(self, content): | |
# print(self.__class__.__name__ + '::update() [content = ' + content +']') | |
pass |
抽象主题类
# Subject.py 定义抽象主题类或者接口,供具体主题类继承 | |
class Subject: | |
def __init__(self, name): | |
self.name = name | |
self.observers = [] | |
def get_name(self): | |
return self.name | |
def set_name(self, name): | |
self.name = name | |
def register(self, observer): | |
print(self.__class__.__name__ + '::register() [observer = ' + | |
observer.__class__.__name__ + ']') | |
self.observers.append(observer) | |
def remove(self, observer): | |
self.observers.remove(observer) | |
# 通知由具体类来实现逻辑 | |
def notify(self, name): | |
pass |
具体主题类
// ConcreteSubject.py 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 | |
from src.Subject import Subject | |
# 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 | |
class ConcreteSubject(Subject): | |
# 不同的主题类有自己的通知方法,批量通知绑定的观察者 | |
def notify(self, content): | |
print(self.__class__.__name__ + '::notify() [content = ' + content + | |
']') | |
for observer in self.observers: | |
observer.update(content) |
测试调用
import sys | |
import os | |
os_path = os.getcwd() | |
sys.path.append(os_path) | |
from src.ConcreteSubject import ConcreteSubject | |
from src.ConcreteObserver import ConcreteObserver | |
from src.ConcreteObserver2 import ConcreteObserver2 | |
def test(): | |
''' | |
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者 | |
* 当发布者发布消息时,批量广播通知,而无需逐一通知 | |
* 观察者监听到消息后自己决定采取哪一种行为 | |
''' | |
# 定义一个主题,也就是发布者 | |
concrete_subject = ConcreteSubject('subject1') | |
# 再声明观察者,通过构造器注册到主题上 | |
observer1 = ConcreteObserver(concrete_subject, 'observer1') | |
# 也可以单独给主题注册一个新的观察者 | |
observer2 = ConcreteObserver2('observer2') | |
concrete_subject.register(observer2) | |
# 可以移除观察者对象 | |
# concrete_subject.remove(observer1) | |
# 主题开始发布新通知,各观察者自动更新 | |
concrete_subject.notify('hello, this is broadcast.') | |
if __name__ == '__main__': | |
print(__file__) | |
print("test start:") | |
test() | |
更多语言版本
不同语言实现设计模式:https://github.com/microwind/design-pattern
相关文章:
【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 观察者模式使用三个类Subject、Observer和Client。Subject…...
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取
精细解析中文公司名称:智能分词工具助力地名、品牌名、行业词和后缀提取 中文公司名称分词工具,支持公司名称中的地名,品牌名(主词),行业词,公司名后缀提取。 对公司名文本解析,识…...
网络编程(JavaEE初阶系列10)
目录 前言: 1.网络编程的基础 1.1为什么需要网络编程 1.2什么是网络编程 1.3网络编程中的基本概念 1.3.1发送端和接收端 1.3.2请求和响应 1.3.3客户端和服务端 2.Socket套接字 2.1概念 2.2分类 3.UDP数据报套接字编程 3.1DataGramSocket API 3.2Datagr…...
Git常用的指令
Git常用的指令 OMMP提交代码的流程 0、配置: git config --list 查看当前配置 git congig --global user.name user 这个会显示你的提交到git的名字 格式:git config [–local|–global|–system] –unset section.key 格式:git config [–l…...
LoadRunner(2)
一、Controller 1.1场景设计 1.通过VUG打开 施压机器:发起请求的角色(用户本地电脑) 被压机器:处理请求的角色(服务器) 2.直接双击Controller 场景设计:需要关注三个部分 第一部分: 第二部分: 2.1运行场景…...
CTF之逆向之阿里巴巴
题目地址:http://www.shiyanbar.com/ctf/13 题目预览: 解题过程: 1、下载附件发现是exe文件 2、使用PEid和Detect It Easy查壳 和 开发语言,发现没有加壳,都是用C#开发的 3、C#和Java Python属于解释型语言ÿ…...
Labview控制APx(Audio Precision)进行测试测量(五)
驱动程序 VIs如何处理配置设置中的单元 APx500 应用程序具有复杂的控件,具有以下功能: 数值和单位组合在一个控制中(例如,1.000 Vrms ) •值转换为 SI 格式(例如,1.000 mVrms 或 1.000 μVrms) •单位之间的转换发生在控制(例如,V…...
在单元测试中使用Jest模拟VS Code extension API
对VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS C…...
django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比
一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…...
谈谈闭包和闭包使用场景
一、什么是闭包 概念:闭包还是作用域的一种特殊应用 二、触发闭包的情况 1.函数当做返回值被返回 2.函数当做参数被传递 3.自执行匿名函数 //情况1:函数当做返回值被返回 function fn(){const a 1;return function(){console.log(a) //1}; } const a …...
MATLAB算法实战应用案例精讲-【图像处理】边界框锚框
目录 目标检测 应用场景 目标检测发展历程 常用数据集 边界框(bounding box)...
04什么场景要用到微服务
一句话导读 根据微服务的特点,可以总结为在构建复杂的、大型的、分布式的、高可用、高并发、高性能的应用时可以使用微服务架构。 目录 一句话导读 一、微服务适用场景 1.业务复杂,模块多且相对独立 2.团队多,管理隔离 3.应用规模大&#…...
.NET SqlSuger 简单介绍,超快开发数据库
文章目录 前言SqlSugar使用我的环境Nuget 安装新建连接串DB First 和 Code First使用增删改查 总结 前言 我之前介绍过EFCore 怎么使用Nuget快速创建数据库,我之后发现SqlSugar更快。这里简单再说一下SqlSugar如何使用 .NET Core 数据库DB First自动生成࿰…...
SpringBoot复习:(28)【前后端不分离】自定义View
一、自定义View package cn.edu.tju.view;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Comp…...
springcloud3 springcloud stream的学习以及案例(了解)
一 springcloud stream的作用 1.1 springcloud stream作用 stream屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。 stream中的消息通信模式遵循了“发布-订阅”模式。 1.2 Binder作用 通过定义绑定器Binder作为中间层,实现…...
Kotlin理解内置函数
目录 一 内置函数1.1 apply 函数1.2 let 函数1.3 run函数1.4 with函数1.5 also函数1.6 takeIf函数1.7 takeUnless函数1.8 总结 Kotlin内置函数包括:let、run、with、apply、also,这些函数都是在Any类中定义的扩展函数,所以任何对象都可以调用…...
手机app测试
一、安装、卸载、更新、运行 1.安装、卸载 应用是否可以正常安装(命令行安装;apk/ipa安装包安装)(有网,无网是否都正常)卸载过程中出现死机,断电,重启等意外的情况&…...
Centos部署Git
Centos部署Git 文章目录 Centos部署Git部署步骤初始化配置免登录 部署步骤 初始化 -- 安装git yum install git配置免登录 配置git下载代码时 每次都需要输入密码的事情 -- 生成 gitconfig 文件 git config --global credential.helper store -- 配置登录邮箱 git config …...
k8s 控制器
Kubernetes(K8S)是一种开源的容器编排平台,它可以自动化地管理容器化应用程序的部署、扩展和运行。K8S中的控制器是一种重要的组件,它可以确保应用程序的状态与期望的状态一致。在K8S中,有五种常见的控制器,…...
谷歌关闭跨域限制.(生成一个开发浏览器),Chrome关闭跨域
(一)、首先找到浏览器在电脑磁盘中的位置,并复制 (二)、复制一个浏览器的快捷方式到桌面(不影响正常浏览器) (三)、chrom鼠标右键属性,修改快捷方式的目标 (四)chrome.exe 后面添加 --disable-web-security --user-data-dir 复制的Chrome浏览…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
