观察者模式简介
概念:
观察者模式(Observer Pattern)是一种行为型设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其相关依赖对象会自动收到通知并进行相应处理。
特点:
- 松耦合:主题和观察者之间通过抽象接口进行交互,使得它们可以独立演化而不影响彼此。
- 一对多关系:一个主题可以有多个观察者,并且它们之间没有直接联系。
- 可扩展性:可以随时增加新的观察者或删除现有观察者。
优点:
- 解耦合:将主题与具体观察者解耦,使得它们可以独立地变化和复用。
- 扩展性:易于添加新的观察者以及定义新的事件类型。
- 实时性:实现了实时更新机制,当主题状态改变时能够即刻通知相关观察者。
缺点:
- 过度使用可能导致性能问题和复杂度增加。
- 触发链问题:如果观察者之间有依赖关系,那么通知链可能会导致不可预料的结果。
适用场景:
- 当一个对象的改变需要同时影响其他多个对象时。
- 当系统中存在一些对象之间的联动行为,但又希望它们之间解耦合时。
实现方式
使用自定义接口
主题和观察者都实现相应接口,在主题中维护一个观察者列表,并在状态改变时遍历通知所有观察者。
实现原理:
- 定义一个观察者接口(Observer),其中声明了一个更新方法(update)用于接收主题状态的改变。
- 定义一个主题接口(Subject),其中包括添加观察者、移除观察者和通知观察者等方法。
- 创建具体的主题类(ConcreteSubject),该类维护了一个观察者列表,并在状态改变时遍历通知所有注册的观察者。
- 创建具体的观察者类(ConcreteObserver),该类实现了更新方法,在收到主题通知时进行相应操作。
实现代码:
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {void update(String newState);
}// 主题接口
interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 具体主题类
class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();private String state;public void setState(String newState) {this.state = newState;notifyObservers();}@Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { observers.remove(observer); } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(state); } }
}// 具体观察者类
class ConcreteObserver implements Observer {private String observerState;@Overridepublic void update(String newState) {this.observerState = newState;// 执行相应操作System.out.println("Observer state updated: " + observerState);}
}// 使用示例
public class Main {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver();Observer observer2 = new ConcreteObserver();subject.registerObserver(observer1);subject.registerObserver(observer2);subject.setState("New State");}
}
上述代码中,我们定义了一个观察者接口 Observer,其中包括了一个更新方法 update。然后,我们定义了一个主题接口 Subject,其中包括注册观察者、移除观察者和通知观察者等方法。接着,我们创建了具体的主题类 ConcreteSubject,该类维护了一个观察者列表,并在状态改变时遍历通知所有注册的观察者。最后,我们创建了具体的观察者类 ConcreteObserver 实现更新方法,在收到主题通知时执行相应操作。
使用自定义接口实现方式的问题:
- 主题与具体观察者之间存在紧耦合关系。
- 观察者可能无法感知到其他已注册的新类型或特定类型。
尽管存在以上问题,使用自定义接口实现方式是观察者模式的经典实现方式,并且具有简单、直观的特点。
使用Java内置Observable类和Observer接口
主题继承Observable类并调用其方法进行状态改变通知,观察者实现Observer接口并注册到主题上。
实现原理:
- 创建一个具体主题类(ConcreteSubject),该类继承自Observable类。
- 在具体主题类中定义状态改变方法,并在该方法中调用setChanged()和notifyObservers()方法来通知所有注册的观察者。
- 创建一个具体观察者类(ConcreteObserver),该类实现了Observer接口,并在update()方法中定义观察者收到通知后的操作。
实现代码:
import java.util.Observable;
import java.util.Observer;// 具体主题类
class ConcreteSubject extends Observable {private String state;public void setState(String newState) {this.state = newState;setChanged();notifyObservers(state);}
}// 具体观察者类
class ConcreteObserver implements Observer {private String observerState;@Overridepublic void update(Observable o, Object arg) {if (o instanceof ConcreteSubject) {this.observerState = (String) arg;// 执行相应操作System.out.println("Observer state updated: " + observerState);}}
}// 使用示例
public class Main {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver();Observer observer2 = new ConcreteObserver();subject.addObserver(observer1);subject.addObserver(observer2);subject.setState("New State");}
}
上述代码中,我们创建了一个具体主题类 ConcreteSubject,该类继承自Java内置的Observable类。在该类中,我们定义了状态改变方法 setState(),并在该方法中调用setChanged()和notifyObservers()来通知所有注册的观察者。
然后,我们创建了一个具体观察者类 ConcreteObserver 实现Observer接口,并在update()方法中定义观察者收到通知后的操作。
最后,在使用示例中,我们创建了一个具体主题对象和两个具体观察者对象,并将观察者注册到主题上。当主题状态改变时,会自动通知所有注册的观察者进行相应操作。使用Java内置Observable和Observer实现方式存在以下问题:
- Observable是一个类而不是接口,因此无法继承其他父类。
- Observable被标记为已过时,虽然仍可使用但不推荐使用。
- 观察者只能通过实现Observer接口来实现与主题的交互。
尽管存在以上问题,使用Java内置Observable和Observer实现方式可以更方便地利用已有工具来快速实现观察者模式。
使用事件机制
通过定义事件类、监听器接口以及注册监听器等方式来实现观察者模式。当事件发生时,主题发布该事件给已注册的监听器。
实现原理:
- 定义一个事件类(Event),该类包含了需要传递给观察者的数据。
- 创建一个主题类(Subject),其中包括注册观察者、移除观察者和触发事件等方法。
- 创建一个具体主题类(ConcreteSubject),该类继承自主题类,在具体主题中定义了相应的业务逻辑,并在合适的时机通过调用触发事件方法来通知所有注册的观察者。
- 创建一个接口或抽象类作为基础,定义了处理特定类型事件的方法,然后创建具体的处理器类,实现这些方法以执行相应操作。
- 观察者根据所需监听的特定类型事件来实现对应接口或抽象类,并在其方法内进行相应操作。
实现代码:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// 事件类
class Event {private String eventType;private Map<String, Object> eventData = new HashMap<>();public Event(String eventType) {this.eventType = eventType;}public String getEventType() {return eventType;}public void addData(String key, Object value) {eventData.put(key, value);}public Object getData(String key) {return eventData.get(key);}
}// 主题接口
interface Subject {void registerObserver(Observer observer, String eventType);void removeObserver(Observer observer, String eventType);void notifyObservers(Event event);
}// 具体主题类
class ConcreteSubject implements Subject {private Map<String, List<Observer>> observersMap = new HashMap<>();@Overridepublic void registerObserver(Observer observer, String eventType) {List<Observer> observers = observersMap.getOrDefault(eventType, new ArrayList<>());observers.add(observer);observersMap.put(eventType, observers);}@Overridepublic void removeObserver(Observer observer, String eventType) {if (observersMap.containsKey(eventType)) {List<Observer> observes = observersMap.get(eventType);observes.remove(observer);if (observes.isEmpty()) { // 若没有观察者监听该事件,则从观察者列表中移除该事件类型observersMap.remove(eventType); }}}@Override public void notifyObservers(Event event) { if (event != null && event.getEventType() != null && observersMap.containsKey(event.getEventType())) {for (Observer observe : observersMap.get(event.getEventType())) { observe.onEventReceived(event); } } }
}// 观察者接口
interface Observer {void onEventReceived(Event event);
}// 具体观察者类1,处理特定类型事件的处理器之一
class ConcreteHandler1 implements Observer {@Override public void onEventReceived(Event event) { if ("eventType1".equals(event.getEventType())) { // 执行相应操作 System.out.println("ConcreteHandler1 received Event: " + event.getData("data")); } }
}// 具体观察者类2,处理特定类型事件的处理器之一
class ConcreteHandler2 implements Observer {@Override public void onEventReceived(Event event) { if ("eventType2".equals(event.getEventType())) { // 执行相应操作 System.out.println("ConcreteHandler2 received Event: " + event.getData("data")); } }
}// 使用示例
public class Main {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer handler1 = new ConcreteHandler1();Observer handler2 = new ConcreteHandler2();subject.registerObserver(handler1, "eventType1");subject.registerObserver(handler2, "eventType2");Event event1 = new Event("eventType1");event1.addData("data", "Event data for eventType1");Event event2 = new Event("eventType2");event2.addData("data", "Event data for eventType2");subject.notifyObservers(event1);subject.notifyObservers(event2);}
}
上述代码中,我们定义了一个事件类 Event,其中包含了需要传递给观察者的数据。然后,我们创建了主题接口 Subject 和具体主题类 ConcreteSubject 来实现注册观察者、移除观察者和通知观察者等方法。
接着,我们定义了一个观察者接口 Observer ,并创建两个具体的处理器类(例如:ConcreteHandler1 和 `ConcreteHandler12)来实现对不同类型事件的监听和相应操作。
在使用示例中,我们创建了一个具体主题对象和两个具体观察者对象,并将观察者注册到主题上。当主题触发特定类型事件时,会自动通知对应的处理器进行相应操作。
使用事件机制实现方式可以更加灵活地处理不同类型的事件,并使得代码结构更清晰。
相关文章:
观察者模式简介
概念: 观察者模式(Observer Pattern)是一种行为型设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其相关依赖对象会自动收到通知并进行相应处理。 特点: 松耦合&a…...
统计程序两个点之间执行的指令数量
环境:支持perf ubuntu安装 apt-get install linux-tools-common linux-tools-generic linux-tools-uname -randroid 一般自带simpleperf 分析 两个点作差, 求中间结果; *(int*)nullptr 0;案例 断点 1 代码 #define SETPOINT(...) do { *(int*)nullptr 0; } while(0…...
时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价)
时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价) 目录 时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于TSO-XGBoost金枪鱼算…...
java- ConcurrentHashMap 并发
1. ConcurrentHashMap 并发 1.1. 减小锁粒度 减小锁粒度是指缩小锁定对象的范围,从而减小锁冲突的可能性,从而提高系统的并发能力。减小锁粒度是一种削弱多线程锁竞争的有效手段,这种技术典型的应用是 ConcurrentHashMap(高性能的 HashMap)…...
java练习8.100m小球落地
题目: 如一个小球从100米高度自由落下,每次落地后就反跳回原高度的一半。 那么求它在第10次落地时,共经过多少米?第10次反弹多高? public static void main(String[] args) {/*假如一个小球从100米高度自由落下,每次落…...
Android JNI系列详解之生成指定CPU的库文件
一、前提 这次主要了解Android的cpu架构类型,以及在使用CMake工具的时候,如何指定生成哪种类型的库文件。 如上图所示,是我们之前使用CMake工具默认生成的四种cpu架构的动态库文件:arm64-v8a、armeabi-v7a、x86、x86_64࿰…...
Leetcode每日一题:1448. 统计二叉树中好节点的数目
原题 给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。 「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。 示例 1: 输入:root [3,1,4,3,null,1,5] 输出:4 解…...
华为OD七日集训第2期 - 按算法分类,由易到难,循序渐进,玩转OD(文末送书)
目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第2期五、精心挑选21道高频100分经典题目,作为入门。第1天、逻辑分析第2天、字符串处理第3天、数据结构第4天、递归回溯第5天、二分查找第6天、深度优先搜索dfs算法第7天、动态规划 六、集训总结1、《代码…...
3d max插件CG MAGIC中的蜂窝材质功能可提升效率吗?
工作中能提升效率也都是大家所想的,对于设计师的一个设计过程中,可能想怎么样可以更快呀,是哪个步骤慢了呢? 这样的结果只能说会很多,但是建模这个步骤,肯定是有多无少的。 为了让模型更加逼真,…...
一句话木马攻击复现:揭示黑客入侵的实战过程
这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。 准备环境 OWASP虚拟机xfp 7与xshell 7 DVWA系统默认的账号密码均为:admin/admin 1、命令注入中复现 攻击payload 127.0.0.1 | echo "<?php eval(…...
【游戏开发教程】Unity Cinemachine快速上手,详细案例讲解(虚拟相机系统 | 新发出品 | 良心教程)
文章目录 一、前言二、插件下载三、案例1:第三人称自由视角,Free Look character场景1、场景演示2、组件参数2.1、CinemachineBrain:核心2.2、CinemachineFreeLook:第三人称自由视角相机2.2.1、设置Follow:跟随2.2.2、…...
当图像宽高为奇数时,如何计算 I420 格式的uv分量大小
背景 I420 中 yuv 数据存放在3个 planes 中。 网上一般说 I420 数据大小为 widthheight1.5 但是当 width 和 height 是奇数时,这个计算公式会有问题。 I420 中 u 和 v 的宽高分别为 y 的一半。 但是当不能整除时,是如何取整呢?向上还是向下&…...
结构型模式-代理模式
代理模式* 定义:在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 意图:为其…...
SpringBoot+Redis BitMap 实现签到与统计功能
最近项目里需要集成签到和统计功能,连续签到后会给用户发放一些优惠券和奖品,以此来吸引用户持续在该品台进行活跃。下面我们一些来聊一聊目前主流的实现方案。 因为签到和统计的功能涉及的数据量比较大,所以在如此大的数据下利用传统的关系…...
P5739 【深基7.例7】计算阶乘
题目描述 求 n ! n! n!,也就是 1 2 3 ⋯ n 1\times2\times3\dots\times n 123⋯n。 挑战:尝试不使用循环语句(for、while)完成这个任务。 输入格式 第一行输入一个正整数 n n n。 输出格式 输出一个正整数,…...
scikit-learn中OneHotEncoder用法
One-Hot编码,又称为一位有效编码,是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值,然后,每个整数值被表示为二进制向量,将整数索引标记为1,其余都标为0。 OneHotEncoder()常用参数解释 …...
linux操作系统的权限的深入学习(未完)
1.Linux权限的概念 Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”,普通用户…...
C 连接MySQL8
Linux 安装MySQL 8 请参考文章:Docker 安装MySQL 8 详解 Visual Studio 2022 编写C 连接MySQL 8 C源码 #include <stdio.h> #include <mysql.h> int main(void) {MYSQL mysql; //数据库句柄MYSQL_RES* res; //查询结果集MYSQL_ROW row; //记录结…...
福利之舞:员工的心跳与企业的平衡术
引言:员工福利与满意度的关系 在现代企业中,员工福利已经不仅仅是一种待遇,而是与员工满意度、忠诚度和生产力紧密相连的关键因素。一个合理且吸引人的福利制度可以大大提高员工的工作积极性,同时也能够吸引和留住顶尖的人才。但…...
MyBatis动态语句且如何实现模糊查询及resultType与resultMap的区别---详细介绍
前言 前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用,因为通常查询条件是多样化的,需要根据实际情况来拼接SQL语句,那什么是MyBatis动态语句呢…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
