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

观察者模式简介

概念

观察者模式(Observer Pattern)是一种行为型设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其相关依赖对象会自动收到通知并进行相应处理。

特点

  1. 松耦合:主题和观察者之间通过抽象接口进行交互,使得它们可以独立演化而不影响彼此。
  2. 一对多关系:一个主题可以有多个观察者,并且它们之间没有直接联系。
  3. 可扩展性:可以随时增加新的观察者或删除现有观察者。

优点

  1. 解耦合:将主题与具体观察者解耦,使得它们可以独立地变化和复用。
  2. 扩展性:易于添加新的观察者以及定义新的事件类型。
  3. 实时性:实现了实时更新机制,当主题状态改变时能够即刻通知相关观察者。

缺点

  1. 过度使用可能导致性能问题和复杂度增加。
  2. 触发链问题:如果观察者之间有依赖关系,那么通知链可能会导致不可预料的结果。

适用场景

  1. 当一个对象的改变需要同时影响其他多个对象时。
  2. 当系统中存在一些对象之间的联动行为,但又希望它们之间解耦合时。

实现方式

使用自定义接口

主题和观察者都实现相应接口,在主题中维护一个观察者列表,并在状态改变时遍历通知所有观察者。

实现原理:

  1. 定义一个观察者接口(Observer),其中声明了一个更新方法(update)用于接收主题状态的改变。
  2. 定义一个主题接口(Subject),其中包括添加观察者、移除观察者和通知观察者等方法。
  3. 创建具体的主题类(ConcreteSubject),该类维护了一个观察者列表,并在状态改变时遍历通知所有注册的观察者。
  4. 创建具体的观察者类(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 实现更新方法,在收到主题通知时执行相应操作。

使用自定义接口实现方式的问题:

  1. 主题与具体观察者之间存在紧耦合关系。
  2. 观察者可能无法感知到其他已注册的新类型或特定类型。

尽管存在以上问题,使用自定义接口实现方式是观察者模式的经典实现方式,并且具有简单、直观的特点。

使用Java内置Observable类和Observer接口

主题继承Observable类并调用其方法进行状态改变通知,观察者实现Observer接口并注册到主题上。

实现原理:

  1. 创建一个具体主题类(ConcreteSubject),该类继承自Observable类。
  2. 在具体主题类中定义状态改变方法,并在该方法中调用setChanged()和notifyObservers()方法来通知所有注册的观察者。
  3. 创建一个具体观察者类(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内置ObservableObserver实现方式存在以下问题:

  1. Observable是一个类而不是接口,因此无法继承其他父类。
  2. Observable被标记为已过时,虽然仍可使用但不推荐使用。
  3. 观察者只能通过实现Observer接口来实现与主题的交互。

尽管存在以上问题,使用Java内置ObservableObserver实现方式可以更方便地利用已有工具来快速实现观察者模式。

使用事件机制

通过定义事件类、监听器接口以及注册监听器等方式来实现观察者模式。当事件发生时,主题发布该事件给已注册的监听器。

实现原理:

  1. 定义一个事件类(Event),该类包含了需要传递给观察者的数据。
  2. 创建一个主题类(Subject),其中包括注册观察者、移除观察者和触发事件等方法。
  3. 创建一个具体主题类(ConcreteSubject),该类继承自主题类,在具体主题中定义了相应的业务逻辑,并在合适的时机通过调用触发事件方法来通知所有注册的观察者。
  4. 创建一个接口或抽象类作为基础,定义了处理特定类型事件的方法,然后创建具体的处理器类,实现这些方法以执行相应操作。
  5. 观察者根据所需监听的特定类型事件来实现对应接口或抽象类,并在其方法内进行相应操作。

实现代码:

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)来实现对不同类型事件的监听和相应操作。

在使用示例中,我们创建了一个具体主题对象和两个具体观察者对象,并将观察者注册到主题上。当主题触发特定类型事件时,会自动通知对应的处理器进行相应操作。

使用事件机制实现方式可以更加灵活地处理不同类型的事件,并使得代码结构更清晰。

相关文章:

观察者模式简介

概念&#xff1a; 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;用于在对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;其相关依赖对象会自动收到通知并进行相应处理。 特点&#xff1a; 松耦合&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. 减小锁粒度 减小锁粒度是指缩小锁定对象的范围&#xff0c;从而减小锁冲突的可能性&#xff0c;从而提高系统的并发能力。减小锁粒度是一种削弱多线程锁竞争的有效手段&#xff0c;这种技术典型的应用是 ConcurrentHashMap(高性能的 HashMap)…...

java练习8.100m小球落地

题目: 如一个小球从100米高度自由落下&#xff0c;每次落地后就反跳回原高度的一半。 那么求它在第10次落地时&#xff0c;共经过多少米&#xff1f;第10次反弹多高&#xff1f; public static void main(String[] args) {/*假如一个小球从100米高度自由落下&#xff0c;每次落…...

Android JNI系列详解之生成指定CPU的库文件

一、前提 这次主要了解Android的cpu架构类型&#xff0c;以及在使用CMake工具的时候&#xff0c;如何指定生成哪种类型的库文件。 如上图所示&#xff0c;是我们之前使用CMake工具默认生成的四种cpu架构的动态库文件&#xff1a;arm64-v8a、armeabi-v7a、x86、x86_64&#xff0…...

Leetcode每日一题:1448. 统计二叉树中好节点的数目

原题 给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&#xff1a;从根到该节点 X 所经过的节点中&#xff0c;没有任何节点的值大于 X 的值。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,3,null,1,5] 输出&#xff1a;4 解…...

华为OD七日集训第2期 - 按算法分类,由易到难,循序渐进,玩转OD(文末送书)

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第2期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、逻辑分析第2天、字符串处理第3天、数据结构第4天、递归回溯第5天、二分查找第6天、深度优先搜索dfs算法第7天、动态规划 六、集训总结1、《代码…...

3d max插件CG MAGIC中的蜂窝材质功能可提升效率吗?

工作中能提升效率也都是大家所想的&#xff0c;对于设计师的一个设计过程中&#xff0c;可能想怎么样可以更快呀&#xff0c;是哪个步骤慢了呢&#xff1f; 这样的结果只能说会很多&#xff0c;但是建模这个步骤&#xff0c;肯定是有多无少的。 为了让模型更加逼真&#xff0c…...

一句话木马攻击复现:揭示黑客入侵的实战过程

这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 准备环境 OWASP虚拟机xfp 7与xshell 7 ​ DVWA系统默认的账号密码均为&#xff1a;admin/admin 1、命令注入中复现 ​ 攻击payload 127.0.0.1 | echo "<?php eval(…...

【游戏开发教程】Unity Cinemachine快速上手,详细案例讲解(虚拟相机系统 | 新发出品 | 良心教程)

文章目录 一、前言二、插件下载三、案例1&#xff1a;第三人称自由视角&#xff0c;Free Look character场景1、场景演示2、组件参数2.1、CinemachineBrain&#xff1a;核心2.2、CinemachineFreeLook&#xff1a;第三人称自由视角相机2.2.1、设置Follow&#xff1a;跟随2.2.2、…...

当图像宽高为奇数时,如何计算 I420 格式的uv分量大小

背景 I420 中 yuv 数据存放在3个 planes 中。 网上一般说 I420 数据大小为 widthheight1.5 但是当 width 和 height 是奇数时&#xff0c;这个计算公式会有问题。 I420 中 u 和 v 的宽高分别为 y 的一半。 但是当不能整除时&#xff0c;是如何取整呢&#xff1f;向上还是向下&…...

结构型模式-代理模式

代理模式* 定义&#xff1a;在代理模式&#xff08;Proxy Pattern&#xff09;中&#xff0c;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口。 意图&#xff1a;为其…...

SpringBoot+Redis BitMap 实现签到与统计功能

最近项目里需要集成签到和统计功能&#xff0c;连续签到后会给用户发放一些优惠券和奖品&#xff0c;以此来吸引用户持续在该品台进行活跃。下面我们一些来聊一聊目前主流的实现方案。 因为签到和统计的功能涉及的数据量比较大&#xff0c;所以在如此大的数据下利用传统的关系…...

P5739 【深基7.例7】计算阶乘

题目描述 求 n ! n! n!&#xff0c;也就是 1 2 3 ⋯ n 1\times2\times3\dots\times n 123⋯n。 挑战&#xff1a;尝试不使用循环语句&#xff08;for、while&#xff09;完成这个任务。 输入格式 第一行输入一个正整数 n n n。 输出格式 输出一个正整数&#xff0c…...

scikit-learn中OneHotEncoder用法

One-Hot编码&#xff0c;又称为一位有效编码&#xff0c;是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值&#xff0c;然后&#xff0c;每个整数值被表示为二进制向量&#xff0c;将整数索引标记为1&#xff0c;其余都标为0。 OneHotEncoder()常用参数解释 …...

linux操作系统的权限的深入学习(未完)

1.Linux权限的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff0c;不受限制 普通用户&#xff1a;在linux下做有限的事情。 超级用户的命令提示符是“#”&#xff0c;普通用户…...

C 连接MySQL8

Linux 安装MySQL 8 请参考文章&#xff1a;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; //记录结…...

福利之舞:员工的心跳与企业的平衡术

引言&#xff1a;员工福利与满意度的关系 在现代企业中&#xff0c;员工福利已经不仅仅是一种待遇&#xff0c;而是与员工满意度、忠诚度和生产力紧密相连的关键因素。一个合理且吸引人的福利制度可以大大提高员工的工作积极性&#xff0c;同时也能够吸引和留住顶尖的人才。但…...

MyBatis动态语句且如何实现模糊查询及resultType与resultMap的区别---详细介绍

前言 前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用&#xff0c;因为通常查询条件是多样化的&#xff0c;需要根据实际情况来拼接SQL语句&#xff0c;那什么是MyBatis动态语句呢…...

麒麟OS国产系统身份证阅读器web网页开发使用操作流程

1、打开麒麟软件商店&#xff0c;选择驱动&#xff0c;找到身份证阅读器&#xff0c;找到东信智能身份证社保卡读卡器&#xff0c;点击安装。 2、安装完成后&#xff0c;点击打开 3、进入读卡界面 4、进入代码集成 <script type"text/javascript">var ctnFin…...

前端面试:【事件处理】探索事件流、委托与事件对象

嗨&#xff0c;亲爱的事件探险家&#xff01;在JavaScript的世界中&#xff0c;事件处理是与用户互动的关键。本文将带你探索事件流、事件委托、常见事件类型和事件对象&#xff0c;这些知识将帮助你成为事件处理的大师。 2. 事件流&#xff1a;事件的旅程 事件流描述了事件从…...

c语言函数指针使用例子

一、是什么? c语言函数名是一段代码首地址,连接器链接时放在text段,下面例子会把函数名打印出来,.map文件内存分布查看相关代码段函数: 下面例子实现步骤: 来源于uboot 的初始化 board_f.c typedef int (*init_fun_t)(void); (1)构建gd数据类型 (2)初始化全局gd变量 (3)实…...

云计算技术应用专业实训室建设方案

一、 云计算技术应用系统概述 云计算技术是一种基于互联网的计算模式&#xff0c;通过将计算资源&#xff08;如服务器、存储、数据库、网络、软件等&#xff09;提供为一种服务&#xff0c;使用户能够按需获取和使用这些资源&#xff0c;而无需拥有和管理实际的物理设备。云计…...

C语言学习之共用体(union)的运用

C语言中的共用体&#xff1a;伪代码表示&#xff1a; union 类型名{ 数据类型1 成员1; 数据类型2 成员2; 数据类型3 成员3; . . . 数据类型n 成员n; };共用体的特点&#xff1a;1.所有的成员是共享同一块内存空间的2.所有成员的首地址是一样的&#xff1b;3.大小取决于共用体中…...

Sentinel 控制台(集群流控管理)

规则配置 要通过 Sentinel 控制台配置集群流控规则&#xff0c;需要对控制台进行改造。我们提供了相应的接口进行适配。 从 Sentinel 1.4.0 开始&#xff0c;我们抽取出了接口用于向远程配置中心推送规则以及拉取规则&#xff1a; DynamicRuleProvider<T>: 拉取规则Dy…...

PMP P-08 Communication Management

...

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结果...

Redis使用

环境配置 代码实现 Java public CoursePublish getCoursePublishCache(Long courseId){//查询缓存Object jsonObj redisTemplate.opsForValue().get("course:" courseId);if(jsonObj!null){String jsonString jsonObj.toString();System.out.println("从缓…...

#systemverilog# 之 event region 和 timeslot 仿真调度(七)Active/NBA 咋跳转的?

目录 一 目的 二 案例分析 2.1 先Active域,后 NBA 域 2.2 先Active域,后 NBA 域,后NBA域...

宝安网站设计制作/seo平台是什么

Java获取系统环境信息 使用System.getProperty()方法获取JVM信息 public class TestSystemGetProperty {public static void main(String[] args) {System.out.println("java版本号&#xff1a;" System.getProperty("java.version"));System.out.println…...

免费铺货诚招代理商/百度seo工具

这次给大家带来js如何直接获取网页中图片地址&#xff0c;js直接获取网页中图片地址的注意事项有哪些&#xff0c;下面就是实战案例&#xff0c;一起来看一下。第一种方法&#xff1a;js通过正则实现/*** 获取html代码中图片地址* param htmlstr* returns {Array}*/function ge…...

温州外贸公司网站建设公司排名/世界十大网站排名

java基础之抽象类&#xff1a; 一、父类不确定性&#xff1a;&#xff08;所谓抽象方法就是没有实现的方法&#xff0c;也就是没有方法体的方法&#xff09; 1.当父类的某些方法&#xff0c;需要声明&#xff0c;但是又不确定如何实现是&#xff0c;可以将其声明为抽象方法&am…...

私自使用他人图片做网站宣传/推广软文

为什么80%的码农都做不了架构师&#xff1f;>>> 用来保存计算最终结果的 数据库是整个信息系统的重要组成部分&#xff0c; 技术也相对成熟。然而&#xff0c;对于所有数据库而言&#xff0c;除了记录正确的 处理结果之外&#xff0c;也面临着一些挑战&#xff1a;…...

苏州园区网站建设/最新军事动态

SQL Server 日期的加减函数: DATEDIFF DATEADD 原文:SQL Server 日期的加减函数: DATEDIFF DATEADD原文地址&#xff1a;http://blog.csdn.net/xyzqiang/article/details/6577831 SQL Server 日期的加减函数: DATEDIFF DATEADDDATEDIFF: 返回跨两个指定日期的日期边界数和时…...

大淘客网站怎样做百度推广/软件网站排行榜

做一个小例子演示滤镜的动态效果 在舞台建两个元件或导入两张图片到舞台再转化为元件&#xff0c;分别取实例名apple01和apple02&#xff1b; var blurSpeed:Number 1;//首先使用动画滤镜 var bevelSpeed:Number 5;//为两个苹果添加MOUSE_OVER 事件侦听 apple01.addEventList…...