观察者模式简介
概念:
观察者模式(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)来实现对不同类型事件的监听和相应操作。
在使用示例中,我们创建了一个具体主题对象和两个具体观察者对象,并将观察者注册到主题上。当主题触发特定类型事件时,会自动通知对应的处理器进行相应操作。
使用事件机制实现方式可以更加灵活地处理不同类型的事件,并使得代码结构更清晰。
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
观察者模式简介
概念: 观察者模式(Observer Pattern)是一种行为型设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其相关依赖对象会自动收到通知并进行相应处理。 特点: 松耦合&a…...
![](https://www.ngui.cc/images/no-images.jpg)
统计程序两个点之间执行的指令数量
环境:支持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…...
![](https://img-blog.csdnimg.cn/d8ec790744514c9c8aa55ac378b26ad9.png#pic_center)
时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价)
时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价) 目录 时序预测 | MATLAB实现基于TSO-XGBoost金枪鱼算法优化XGBoost的时间序列预测(多指标评价)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于TSO-XGBoost金枪鱼算…...
![](https://img-blog.csdnimg.cn/7f21b181b4c445d389f2790a2a365d09.png)
java- ConcurrentHashMap 并发
1. ConcurrentHashMap 并发 1.1. 减小锁粒度 减小锁粒度是指缩小锁定对象的范围,从而减小锁冲突的可能性,从而提高系统的并发能力。减小锁粒度是一种削弱多线程锁竞争的有效手段,这种技术典型的应用是 ConcurrentHashMap(高性能的 HashMap)…...
![](https://img-blog.csdnimg.cn/1ee677f14dde4b0f887a5335d92dc499.png)
java练习8.100m小球落地
题目: 如一个小球从100米高度自由落下,每次落地后就反跳回原高度的一半。 那么求它在第10次落地时,共经过多少米?第10次反弹多高? public static void main(String[] args) {/*假如一个小球从100米高度自由落下,每次落…...
![](https://img-blog.csdnimg.cn/2796c6facf5b4a268cb67de3989d7391.png)
Android JNI系列详解之生成指定CPU的库文件
一、前提 这次主要了解Android的cpu架构类型,以及在使用CMake工具的时候,如何指定生成哪种类型的库文件。 如上图所示,是我们之前使用CMake工具默认生成的四种cpu架构的动态库文件:arm64-v8a、armeabi-v7a、x86、x86_64࿰…...
![](https://img-blog.csdnimg.cn/img_convert/c5ac6813be35290d5c6e0d50062d419b.png)
Leetcode每日一题:1448. 统计二叉树中好节点的数目
原题 给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。 「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。 示例 1: 输入:root [3,1,4,3,null,1,5] 输出:4 解…...
![](https://img-blog.csdnimg.cn/5f248245bb264a90b4a644489cb225f8.png#pic_center)
华为OD七日集训第2期 - 按算法分类,由易到难,循序渐进,玩转OD(文末送书)
目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第2期五、精心挑选21道高频100分经典题目,作为入门。第1天、逻辑分析第2天、字符串处理第3天、数据结构第4天、递归回溯第5天、二分查找第6天、深度优先搜索dfs算法第7天、动态规划 六、集训总结1、《代码…...
![](https://img-blog.csdnimg.cn/fccfbca341124b50b28c5f30fc9fda98.png)
3d max插件CG MAGIC中的蜂窝材质功能可提升效率吗?
工作中能提升效率也都是大家所想的,对于设计师的一个设计过程中,可能想怎么样可以更快呀,是哪个步骤慢了呢? 这样的结果只能说会很多,但是建模这个步骤,肯定是有多无少的。 为了让模型更加逼真,…...
![](https://img-blog.csdnimg.cn/c8e2f3a3ee2b4bae92b6e4c53951d155.png)
一句话木马攻击复现:揭示黑客入侵的实战过程
这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。 准备环境 OWASP虚拟机xfp 7与xshell 7 DVWA系统默认的账号密码均为:admin/admin 1、命令注入中复现 攻击payload 127.0.0.1 | echo "<?php eval(…...
![](https://img-blog.csdnimg.cn/5146a01b4fcd452f84b4bcaaadb78a64.gif)
【游戏开发教程】Unity Cinemachine快速上手,详细案例讲解(虚拟相机系统 | 新发出品 | 良心教程)
文章目录 一、前言二、插件下载三、案例1:第三人称自由视角,Free Look character场景1、场景演示2、组件参数2.1、CinemachineBrain:核心2.2、CinemachineFreeLook:第三人称自由视角相机2.2.1、设置Follow:跟随2.2.2、…...
![](https://img-blog.csdnimg.cn/d6f22a0be90840618c34ac671b5e706d.png)
当图像宽高为奇数时,如何计算 I420 格式的uv分量大小
背景 I420 中 yuv 数据存放在3个 planes 中。 网上一般说 I420 数据大小为 widthheight1.5 但是当 width 和 height 是奇数时,这个计算公式会有问题。 I420 中 u 和 v 的宽高分别为 y 的一半。 但是当不能整除时,是如何取整呢?向上还是向下&…...
![](https://img-blog.csdnimg.cn/398c642486da4694894fafd08e1c824c.png)
结构型模式-代理模式
代理模式* 定义:在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 意图:为其…...
![](https://www.ngui.cc/images/no-images.jpg)
SpringBoot+Redis BitMap 实现签到与统计功能
最近项目里需要集成签到和统计功能,连续签到后会给用户发放一些优惠券和奖品,以此来吸引用户持续在该品台进行活跃。下面我们一些来聊一聊目前主流的实现方案。 因为签到和统计的功能涉及的数据量比较大,所以在如此大的数据下利用传统的关系…...
![](https://www.ngui.cc/images/no-images.jpg)
P5739 【深基7.例7】计算阶乘
题目描述 求 n ! n! n!,也就是 1 2 3 ⋯ n 1\times2\times3\dots\times n 123⋯n。 挑战:尝试不使用循环语句(for、while)完成这个任务。 输入格式 第一行输入一个正整数 n n n。 输出格式 输出一个正整数,…...
![](https://img-blog.csdnimg.cn/fa6e5922f4a34408ac88294925192d71.png)
scikit-learn中OneHotEncoder用法
One-Hot编码,又称为一位有效编码,是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值,然后,每个整数值被表示为二进制向量,将整数索引标记为1,其余都标为0。 OneHotEncoder()常用参数解释 …...
![](https://img-blog.csdnimg.cn/cb30c44aa40b4f53b35a50853be830de.png)
linux操作系统的权限的深入学习(未完)
1.Linux权限的概念 Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”,普通用户…...
![](https://img-blog.csdnimg.cn/5c7eb49155fe4a3abd95ec046c865344.png)
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; //记录结…...
![](https://img-blog.csdnimg.cn/fb3d1a99cf664d0cb0162ca757b76c71.png#pic_center)
福利之舞:员工的心跳与企业的平衡术
引言:员工福利与满意度的关系 在现代企业中,员工福利已经不仅仅是一种待遇,而是与员工满意度、忠诚度和生产力紧密相连的关键因素。一个合理且吸引人的福利制度可以大大提高员工的工作积极性,同时也能够吸引和留住顶尖的人才。但…...
![](https://img-blog.csdnimg.cn/e1980129c836411badd8ceafda90f68a.png)
MyBatis动态语句且如何实现模糊查询及resultType与resultMap的区别---详细介绍
前言 前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用,因为通常查询条件是多样化的,需要根据实际情况来拼接SQL语句,那什么是MyBatis动态语句呢…...
![](https://img-blog.csdnimg.cn/f6aec104a680458c9a676599a1a9164a.jpeg)
麒麟OS国产系统身份证阅读器web网页开发使用操作流程
1、打开麒麟软件商店,选择驱动,找到身份证阅读器,找到东信智能身份证社保卡读卡器,点击安装。 2、安装完成后,点击打开 3、进入读卡界面 4、进入代码集成 <script type"text/javascript">var ctnFin…...
![](https://www.ngui.cc/images/no-images.jpg)
前端面试:【事件处理】探索事件流、委托与事件对象
嗨,亲爱的事件探险家!在JavaScript的世界中,事件处理是与用户互动的关键。本文将带你探索事件流、事件委托、常见事件类型和事件对象,这些知识将帮助你成为事件处理的大师。 2. 事件流:事件的旅程 事件流描述了事件从…...
![](https://www.ngui.cc/images/no-images.jpg)
c语言函数指针使用例子
一、是什么? c语言函数名是一段代码首地址,连接器链接时放在text段,下面例子会把函数名打印出来,.map文件内存分布查看相关代码段函数: 下面例子实现步骤: 来源于uboot 的初始化 board_f.c typedef int (*init_fun_t)(void); (1)构建gd数据类型 (2)初始化全局gd变量 (3)实…...
![](https://img-blog.csdnimg.cn/img_convert/4235c3ee40d43346e2f33ce47a432a2c.png)
云计算技术应用专业实训室建设方案
一、 云计算技术应用系统概述 云计算技术是一种基于互联网的计算模式,通过将计算资源(如服务器、存储、数据库、网络、软件等)提供为一种服务,使用户能够按需获取和使用这些资源,而无需拥有和管理实际的物理设备。云计…...
![](https://www.ngui.cc/images/no-images.jpg)
C语言学习之共用体(union)的运用
C语言中的共用体:伪代码表示: union 类型名{ 数据类型1 成员1; 数据类型2 成员2; 数据类型3 成员3; . . . 数据类型n 成员n; };共用体的特点:1.所有的成员是共享同一块内存空间的2.所有成员的首地址是一样的;3.大小取决于共用体中…...
![](https://img-blog.csdnimg.cn/568d7d5cab0647a28449d0c66b46a022.png)
Sentinel 控制台(集群流控管理)
规则配置 要通过 Sentinel 控制台配置集群流控规则,需要对控制台进行改造。我们提供了相应的接口进行适配。 从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则: DynamicRuleProvider<T>: 拉取规则Dy…...
![](https://img-blog.csdnimg.cn/23377bd747b74ac2af691383e1f61dd2.png)
matlab中判断数据的奇偶性(mod函数、rem函数)
用Matlab判断一个数是偶数还是奇数 1、mod函数 X 25;%要判断的数 if mod(X,2)1disp(奇数);%奇数 elsedisp(偶数);%偶数 end结果 2、rem函数 n25; if rem(n,2)0display(偶数); elsedisplay(奇数); end结果...
![](https://img-blog.csdnimg.cn/72962ea7f21745f898d07b17b61e7542.png)
Redis使用
环境配置 代码实现 Java public CoursePublish getCoursePublishCache(Long courseId){//查询缓存Object jsonObj redisTemplate.opsForValue().get("course:" courseId);if(jsonObj!null){String jsonString jsonObj.toString();System.out.println("从缓…...
![](https://www.ngui.cc/images/no-images.jpg)
#systemverilog# 之 event region 和 timeslot 仿真调度(七)Active/NBA 咋跳转的?
目录 一 目的 二 案例分析 2.1 先Active域,后 NBA 域 2.2 先Active域,后 NBA 域,后NBA域...
![](/images/no-images.jpg)
宝安网站设计制作/seo平台是什么
Java获取系统环境信息 使用System.getProperty()方法获取JVM信息 public class TestSystemGetProperty {public static void main(String[] args) {System.out.println("java版本号:" System.getProperty("java.version"));System.out.println…...
![](mall/kmenus/001.png)
免费铺货诚招代理商/百度seo工具
这次给大家带来js如何直接获取网页中图片地址,js直接获取网页中图片地址的注意事项有哪些,下面就是实战案例,一起来看一下。第一种方法:js通过正则实现/*** 获取html代码中图片地址* param htmlstr* returns {Array}*/function ge…...
![](/images/no-images.jpg)
温州外贸公司网站建设公司排名/世界十大网站排名
java基础之抽象类: 一、父类不确定性:(所谓抽象方法就是没有实现的方法,也就是没有方法体的方法) 1.当父类的某些方法,需要声明,但是又不确定如何实现是,可以将其声明为抽象方法&am…...
![](http://static.oschina.net/uploads/img/201305/17163445_s1Bj.jpg)
私自使用他人图片做网站宣传/推广软文
为什么80%的码农都做不了架构师?>>> 用来保存计算最终结果的 数据库是整个信息系统的重要组成部分, 技术也相对成熟。然而,对于所有数据库而言,除了记录正确的 处理结果之外,也面临着一些挑战:…...
![](/images/no-images.jpg)
苏州园区网站建设/最新军事动态
SQL Server 日期的加减函数: DATEDIFF DATEADD 原文:SQL Server 日期的加减函数: DATEDIFF DATEADD原文地址:http://blog.csdn.net/xyzqiang/article/details/6577831 SQL Server 日期的加减函数: DATEDIFF DATEADDDATEDIFF: 返回跨两个指定日期的日期边界数和时…...
![](/images/no-images.jpg)
大淘客网站怎样做百度推广/软件网站排行榜
做一个小例子演示滤镜的动态效果 在舞台建两个元件或导入两张图片到舞台再转化为元件,分别取实例名apple01和apple02; var blurSpeed:Number 1;//首先使用动画滤镜 var bevelSpeed:Number 5;//为两个苹果添加MOUSE_OVER 事件侦听 apple01.addEventList…...