java:观察者模式

java:观察者模式

1 前言

观察者模式,又被称为发布-订阅(Publish/Subscribe)模式,他定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

2 使用

(2.1)结构

在观察者模式中有如下角色:

Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

ConcreteObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

(2.2)案例

如公众号使用场景,当多个用户关注了某个公众号时,当公众号有内容更新时,会推送给关注了公众号的多个用户。这里,多个用户就是观察者,公众号就是被观察者。

下面以Java为例来实现一个简单的观察者模式:

在这里插入图片描述

Subject接口:

/*** 抽象主题角色类*/
public interface Subject {// 添加订阅者(添加观察者对象)void attach(Observer observer);// 删除订阅者void detach(Observer observer);// 通知订阅者更新消息void notify(String message, String status);}

SubscriptionSubject:

public class SubscriptionSubject implements Subject{List<Observer> users = new ArrayList<>();@Overridepublic void attach(Observer observer) {users.add(observer);}@Overridepublic void detach(Observer observer) {users.remove(observer);}@Overridepublic void notify(String message, String status) {//  遍历集合for (Observer user : users) {//  调用观察者对象中的update方法user.update(message, status);}}
}

Observer:

/*** @author xiaoxu* @date 2024-04-20 19:43* learn_java:com.xiaoxu.design.observe.Observer* 抽象观察者类*/
public interface Observer {void update(String message, String status);}

AbstractObserver:

public abstract class AbstractObserver implements Observer{protected String status;public AbstractObserver(String status) {this.status = status;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}
}

ObserveUser:

public class ObserveUser extends AbstractObserver{private String name;public ObserveUser(String name, String status) {super(status);this.name = name;}@Overridepublic void update(String message, String status) {System.out.println(String.format("%s接收到消息:%s.原本状态为:%s," +"更新后的状态为:%s.",this.name, message,this.getStatus(), status));this.setStatus(status);}
}

Client(验证上述的效果):

public class Client {public static void main(String[] args) {// 创建公众号对象SubscriptionSubject subject = new SubscriptionSubject();//  创建订阅者,订阅公众号subject.attach(new ObserveUser("小徐", "online"));subject.attach(new ObserveUser("小李", "offline"));//  公众号更新,发出消息给订阅者(观察者对象)subject.notify("来看心世界", "receive");}}

执行结果如下:

小徐接收到消息:来看心世界.原本状态为:online,更新后的状态为:receive.
小李接收到消息:来看心世界.原本状态为:offline,更新后的状态为:receive.

(2.3)优缺点

优点:

(1)降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。

(2)被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】。

缺点:

(1)如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时。

(2)如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃。

(2.4)使用场景

(1)对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。

(2)当一个抽象模型有两个方面,其中一个方面依赖于另一方面时。

(2.5)JDK中提供的实现

在Java中,通过java.util.Observable类和java.util.Observer接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

(1)Observable类

Observable类是抽象目标类(被观察者),它有一个Vector集合成员变量,用于保存所有要通知的观察者对象,下面来介绍它最重要的3个方法:

public synchronized void addObserver(Observer o)方法:用于将新的观察者对象添加到集合中。

public void notifyObservers(Object arg)方法:调用集合中的所有观察者对象的update方法,通知它们数据放生改变。通常越晚加入集合的观察者越先得到通知(因为JDK源码是索引从大到小遍历集合中的观察者)。

protected synchronized void setChanged()方法:用来设置一个boolean类型的内部标志,注明目标对象发生了变化。当它为true时,public void notifyObservers(Object arg)才会通知观察者。

JDK的完整Observable类实现如下:

package java.util;public class Observable {private boolean changed = false;private Vector<Observer> obs;/** Construct an Observable with zero Observers. */public Observable() {obs = new Vector<>();}/*** Adds an observer to the set of observers for this object, provided* that it is not the same as some observer already in the set.* The order in which notifications will be delivered to multiple* observers is not specified. See the class comment.** @param   o   an observer to be added.* @throws NullPointerException   if the parameter o is null.*/public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}/*** Deletes an observer from the set of observers of this object.* Passing <CODE>null</CODE> to this method will have no effect.* @param   o   the observer to be deleted.*/public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}/*** If this object has changed, as indicated by the* <code>hasChanged</code> method, then notify all of its observers* and then call the <code>clearChanged</code> method to* indicate that this object has no longer changed.* <p>* Each observer has its <code>update</code> method called with two* arguments: this observable object and <code>null</code>. In other* words, this method is equivalent to:* <blockquote><tt>* notifyObservers(null)</tt></blockquote>** @see     java.util.Observable#clearChanged()* @see     java.util.Observable#hasChanged()* @see     java.util.Observer#update(java.util.Observable, java.lang.Object)*/public void notifyObservers() {notifyObservers(null);}/*** If this object has changed, as indicated by the* <code>hasChanged</code> method, then notify all of its observers* and then call the <code>clearChanged</code> method to indicate* that this object has no longer changed.* <p>* Each observer has its <code>update</code> method called with two* arguments: this observable object and the <code>arg</code> argument.** @param   arg   any object.* @see     java.util.Observable#clearChanged()* @see     java.util.Observable#hasChanged()* @see     java.util.Observer#update(java.util.Observable, java.lang.Object)*/public void notifyObservers(Object arg) {/** a temporary array buffer, used as a snapshot of the state of* current Observers.*/Object[] arrLocal;synchronized (this) {/* We don't want the Observer doing callbacks into* arbitrary code while holding its own Monitor.* The code where we extract each Observable from* the Vector and store the state of the Observer* needs synchronization, but notifying observers* does not (should not).  The worst result of any* potential race-condition here is that:* 1) a newly-added Observer will miss a*   notification in progress* 2) a recently unregistered Observer will be*   wrongly notified when it doesn't care*/if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}/*** Clears the observer list so that this object no longer has any observers.*/public synchronized void deleteObservers() {obs.removeAllElements();}/*** Marks this <tt>Observable</tt> object as having been changed; the* <tt>hasChanged</tt> method will now return <tt>true</tt>.*/protected synchronized void setChanged() {changed = true;}/*** Indicates that this object has no longer changed, or that it has* already notified all of its observers of its most recent change,* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.* This method is called automatically by the* <code>notifyObservers</code> methods.** @see     java.util.Observable#notifyObservers()* @see     java.util.Observable#notifyObservers(java.lang.Object)*/protected synchronized void clearChanged() {changed = false;}/*** Tests if this object has changed.** @return  <code>true</code> if and only if the <code>setChanged</code>*          method has been called more recently than the*          <code>clearChanged</code> method on this object;*          <code>false</code> otherwise.* @see     java.util.Observable#clearChanged()* @see     java.util.Observable#setChanged()*/public synchronized boolean hasChanged() {return changed;}/*** Returns the number of observers of this <tt>Observable</tt> object.** @return  the number of observers of this object.*/public synchronized int countObservers() {return obs.size();}
}

(2)Observer接口

Observer接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用update方法,进行相应的工作。

JDK中Observer接口如下所示:

public interface Observer {/*** This method is called whenever the observed object is changed. An* application calls an <tt>Observable</tt> object's* <code>notifyObservers</code> method to have all the object's* observers notified of the change.** @param   o     the observable object.* @param   arg   an argument passed to the <code>notifyObservers</code>*                 method.*/void update(Observable o, Object arg);
}

举个栗子,警察抓小偷:

警察抓小偷可以使用观察者模式来实现,警察是观察者,小偷是被观察者,代码如下:

小偷是一个被观察者,所以需要继承Observable类:

public class Thief extends Observable {private String name;public Thief(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public void steal() {System.out.println("小偷" + this.name+ ": 我偷了隔壁的珍珠奶茶," +"有没有人来抓我。");super.setChanged();     // changed = truesuper.notifyObservers();}
}

警察是一个观察者,所以需要让其实现Observer接口:

public class Policemen implements Observer {private String name;public Policemen(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic void update(Observable o, Object arg) {System.out.println("小偷传的arg是null" +"(notifyObservers方法的arg参数):" + arg);System.out.println("警察" + this.name + ":"+ ((Thief) o).getName() +", 站住,你已经被包围了!双脚抱头,倒立站好!");}
}

Client调用:

public class TPClient {public static void main(String[] args) {//  创建小偷对象Thief t = new Thief("采花大盗");//  创建警察对象Policemen policemen = new Policemen("小徐");// 让警察盯着小偷(为小偷添加观察者)t.addObserver(policemen);//  小偷偷东西(被观察者发布事件,会被观察者发现)t.steal();}}

执行结果如下:

小偷采花大盗: 我偷了隔壁的珍珠奶茶,有没有人来抓我。
小偷传的arg是null(notifyObservers方法的arg参数):null
警察小徐:采花大盗, 站住,你已经被包围了!双脚抱头,倒立站好!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2981953.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

邂逅JavaScript逆向爬虫-------基础篇之面向对象

目录 一、概念二、对象的创建和操作2.1 JavaScript创建对象的方式2.2 对象属性操作的控制2.3 理解JavaScript创建对象2.3.1 工厂模式2.3.2 构造函数2.3.3 原型构造函数 三、继承3.1 通过原型链实现继承3.2 借用构造函数实现继承3.3 寄生组合式继承3.3.1 对象的原型式继承3.3.2 …

Java | Leetcode Java题解之第48题旋转图像

题目&#xff1a; 题解&#xff1a; class Solution {public void rotate(int[][] matrix) {int n matrix.length;// 水平翻转for (int i 0; i < n / 2; i) {for (int j 0; j < n; j) {int temp matrix[i][j];matrix[i][j] matrix[n - i - 1][j];matrix[n - i - 1]…

YOLOv8 训练自己的数据集(20240423)

环境搭建请参考&#xff1a;Win10 搭建 YOLOv8 运行环境&#xff08;20240423&#xff09;-CSDN博客 环境测试请参考&#xff1a;本地运行测试 YOLOv8&#xff08;20240423&#xff09;-CSDN博客 一、使用 YOLOv8 的 coco128 数据集熟悉一下如何训练和预测 1.1、在项目根目录…

二手车交易平台搭建重点,会用到哪些三方服务?

在搭建二手车交易平台时&#xff0c;有几个重点方面需要关注&#xff0c;并且会涉及到一些第三方服务的使用。以下是关键点和可能用到的第三方服务&#xff1a; 一、二手车交易平台搭建重点 用户友好与界面设计&#xff1a;一个成功的二手车交易平台首先需要一个直观、易用且吸…

【软件安装】(十六)双系统Ubuntu22.04引导启动菜单的默认项

一个愿意伫立在巨人肩膀上的农民...... 好学的人总是喜欢在电脑上安装双系统&#xff0c;可是安装好系统之后&#xff0c;就会出现默认启动优先级的苦恼&#xff0c;如果在Bios中设置Windows引导启动为优先启动&#xff0c;那么每次想要进如Ubuntu系统就都需要重新设置Bios。如…

LAMMPS单层石墨烯建模

本文主要介绍两种晶胞建模方式。 一、Z形晶胞 晶胞分析&#xff1a;a1沿水平x轴方向&#xff0c;a2沿垂直y轴方向。石墨烯是二维结构&#xff0c;a3取小于单层石墨烯厚度。假设石墨烯键长L1.421&#xff0c;则a13L&#xff0c;a21.732L&#xff0c;a32L&#xff08;低于3.35即…

CSAPP | Lab2-Bomb Lab详细解析

预备阶段 1.Lab要求 邪恶的邪恶博士在我们班的机器上安放了大量的“二元炸弹”。二进制炸弹是一个由一系列阶段组成的程序。每个阶段都要求你在 stdin 上键入一个特定的字符串。如果你键入了正确的字符串&#xff0c;那么这个阶段就会被拆除&#xff0c;炸弹就会进入下一个阶…

如何利用美国站群服务器通过CN2线路优化中美之间的数据传输?

如何利用美国站群服务器通过CN2线路优化中美之间的数据传输? 随着全球化进程的不断推进&#xff0c;跨国企业和国际市场的拓展对数据传输速度和稳定性提出了更高的要求。特别是对于中美之间的数据传输&#xff0c;由于地理位置遥远和网络环境不同&#xff0c;优化数据传输变得…

数据类型总结

1 引言 在计算机的世界里&#xff0c;数据类型是被人类定义出来的&#xff0c;方便人去更好地理解、辨别数据。计算机只能识别二进制数&#xff0c;不可能要求写代码时&#xff0c;只是输入一些0/1的东西。通过定义数据类型&#xff0c;可以让人和计算机更好地“沟通”&#x…

制氢机远程监控运维方案

制氢机远程监控运维方案 在当今能源转型的大背景下&#xff0c;氢能作为清洁、高效且可再生的能源载体&#xff0c;其重要性日益凸显。而制氢机作为氢能产业链中的关键设备&#xff0c;其稳定运行与高效运维对于保障氢气供应、推动氢能产业健康发展至关重要。在此背景下&#…

spring boot 基础案例【2】对多环境配置的支持更改

教程1 案例教程 案例仓库 在线编程 教程2 基础教程 教程仓库 在线编程 本案例所在的仓库 本案例所在的文档 进入正文 1.文件目录 1. Chapter12Application.java 地址&#xff1a;/chapter1-2/src/main/java/com/didispace/chapter12/Chapter12Application.java package com.…

康谋分享 | aiSim5激光雷达LiDAR模型验证方法(二)

aiSim中的LiDAR是一种基于光线追踪的传感器&#xff0c;能够模拟真实LiDAR发射的激光束&#xff0c;将会生成LAS v1.4标准格式的3D点云&#xff0c;包含了方位角、俯仰角和距离等。 aiSim能够模拟LiDAR单态&#xff08;Monostatic&#xff09;和同轴&#xff08;Coaxial&#…

PC端微信软件如何多开【详细教程】

现在工作中&#xff0c;很多小伙伴会用到两个微信。如何在PC端同时登录多个微信呢&#xff1f;赶快跟着下面的教程学起来吧 1、创建一个txt文本文件 2、输入以下代码并保存 echo offstart "" "复制粘贴微信的目标地址" 需要开几个微信就复制几行exit示例…

UTONMOS:用区块链技术拓展商业边界在哪里?

引言 大约从 2021 年Web 3 这个新概念开始受到风险基金和科技圈的普遍关注。但如果你对过去几年区块链的发展历史足够了解&#xff0c;就应该已经意识到现在的 Web 3 并不是什么新技术&#xff0c;甚至不是旧技术的进步&#xff0c;它只是一个基于区块链技术的宏大构想。 我是…

Vue3+Vant开发:个人信息管理

🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎 📚系列专栏:Java全栈,计算机系列(火速更新中) 💭 格言:种一棵树最好的时间是十年前,其次是现在 🏡动动小手,点个关注不迷路,…

一个联合均值与方差模型的R包——dglm

目录 一、引言二、包的安装与载入三、模拟例子3.1 数据生成3.2 数据查看3.3 模型估计参数 一、引言 在 R 语言中&#xff0c;dglm 包是用于拟合双参数广义线性模型&#xff08;Double Generalized Linear Models&#xff0c;简称 DGLMs&#xff09;的一个工具。这类模型允许同…

C语言实现双人贪吃蛇项目(基于控制台界面)

一.贪吃蛇 贪吃蛇是一款简单而富有乐趣的游戏&#xff0c;它的规则易于理解&#xff0c;但挑战性也很高。它已经成为经典的游戏之一&#xff0c;并且在不同的平台上一直受到人们的喜爱和回忆。 二.贪吃蛇的功能 游戏控制&#xff1a;玩家可以使用键盘输入设备来控制蛇的移动方…

139GB,台北倾斜摄影OSGB数据V0.1版

本月初发布了谷歌倾斜摄影数据OSGB转换工具V0.2版(更新&#xff01;谷歌倾斜摄影转换生成OSGB瓦片V0.2版),并免费分享了基于V0.2版转换工具生产的澳门地区OSGB数据(首发&#xff01;澳门地区OSGB数据V0.2版免费分享),V0.2版本在生产速度、显示效率和OSGB数据轻量化方面进行了优…

NVIDIA Jetson jtop查看资源信息

sudo -H pip install -U jetson-stats 安装好之后可能需要reboot 执行jtop&#xff1a; 时间久了可能会退出&#xff0c;可参考如下再次启动。 nvidiategra-ubuntu:~$ jtop The jtop.service is not active. Please run: sudo systemctl restart jtop.service nvidiategra-ub…

修改docker镜像版本,容器大小缩小10%!

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 是的&#xff0c;你看的没错&#xff1a;修改docker镜像的版本&#xff0c;我的…