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://www.mzph.cn/bicheng/2752.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百面算法工程师 | 卷积基础知识——Convolution

目录 8.1 图像卷积过程 8.2 卷积层基本参数 8.3 卷积后图像的长和宽大小的计算方式 8.4 卷积神经网络中的权重共享 8.5 上采样中的反卷积 8.6 空洞卷积 8.7 深度可分离卷积 8.8 为什么可分离卷积中Depthwise卷积后还要进行pointwise卷积 8.9 分组卷积 Group Conv 8.1…

AI视频教程下载:ChatGPT个人生产力提升指南

在本课程中&#xff0c;您将学习如何成功地将ChatGPT融入您的工作和生活&#xff0c;以优化您的社交媒体内容并扩展您的业务。 您将学习如何使用ChatGPT自动创建、校对、总结和翻译文本&#xff0c;以及如何与机器人沟通以实现您的目标。 您还将学习如何创建jQuery代码、编程…

倪海厦是怎么去思考问题的(一)下

1《天纪》是自然法则 2自然法则是个《真理》 3《真理》不需要再证实 4《真理》没有二元对立 紧接着第三点&#xff1a;真理不需要再去证实。现在有很多的人呢&#xff0c;看书学习&#xff0c;自认为自己很聪明&#xff0c;总要去证实一些东西。证明谁的说法是错的&#xff…

麒麟龙芯loongarch64 electron 打包deb包

在麒麟龙芯&#xff08;loongarch64&#xff09;电脑上 使用electron 开发桌面应用。之前用electron-packager 打包出来的是文件夹 是 unpack 包。现在需要打包deb包&#xff0c;依据开发指南开始打包。 在项目文件夹下 打开终端 输入 npm run packager 先打包unpack包 然后…

ATFX汇市:澳大利亚一季度CPI年率大降,澳元升值态势延续

▲ATFX图 据澳大利亚统计局数据&#xff0c;第一季度CPI年率最新值3.6%&#xff0c;相比前值4.1%大降0.5个百分点&#xff0c;但仍高于3.5%的预期值。历史数据看&#xff0c;自从2022年四季度创出7.8%阶段性高点之后&#xff0c;澳大利亚的季度CPI年率就保持着稳定的下降态势。…

翻译《The Old New Thing》 - Why are HANDLE return values so inconsistent?

Why are HANDLE return values so inconsistent? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040302-00/?p40443 Raymond Chen 2004年01月27日 简介 在处理 Windows 编程中的句柄时&#xff0c;开发者需要面对的一个挑战是不同函数可…

SWCTF

easy_php 源码 <?php// flag is in flag.php highlight_file(__FILE__); ini_set(display_errors, 0); error_reporting(0);if (isset($_GET[myon1]) && isset($_GET[myon2]) && isset($_GET[myon3])) {$myon1 $_GET[myon1];$myon2 $_GET[myon2];$myon…

苍穹外卖day12 (Apache POI) 数据统计-Excel报表

文章目录 前言一、工作台1.1 今日数据1.1.1 接口设计1.1.2 代码实现 1.2 订单管理接口1.3菜品总览接口1.4 套餐总览接口1.5 订单搜索&#xff08;已完成&#xff09;1.6 各个状态的订单数量统计&#xff08;已完成&#xff09; 二、Apache POI2.1 概述2.2 效果展示 三、导出运营…

vue中web端播放rtsp视频流(摄像头监控视频)(海康威视录像机)

一、ffmpeg安装​​​​​​ ffmpeg下载 https://ffmpeg.org/download.html找ffmpeg-release-essentials.zip点击下载&#xff0c;下载完解压ffmpeg.exe 程序运行 二、配置ffmpeg环境变量 添加成功后验证是否生效任意地方打开cmd窗口输入 ffmpeg 打印如下表示成功 三、node…

未来五十年,智能科技将如何改变传统行业格局?

未来五十年内&#xff0c;随着人工智能&#xff08;AI&#xff09;和智能科技的不断发展&#xff0c;许多行业将面临被取代的风险。虽然这种趋势可能会带来一些担忧&#xff0c;但也将为人类社会带来巨大的变革。下面将详细探讨哪些行业可能会在未来被智能科技所取代。 ▶ 制造…

利用弹性云主机部署高效数据库系统

在当今云计算的时代&#xff0c;弹性云主机&#xff08;EC2&#xff09;为我们提供了前所未有的灵活性和可扩展性&#xff0c;使得在云端部署高效的数据库系统成为可能。本文将从选择适当的云服务配置、优化数据库设置、建立完备的数据备份与恢复策略&#xff0c;以及加强数据库…

ardupilot开发 --- Jetson Orin Nano 篇

多情应笑我早生华发 0. 一些概念1. 系统安装&#xff08;刷机、flash&#xff09;1.1 使用SD卡安装系统1.2 使用固态硬盘安装系统 0. 一些概念 官网&#xff1a;https://www.nvidia.com/en-us/ Developers Documentation Getting Started Jetson Developer Kits User Guid…

「C++ 内存管理篇 1」C++动态内存分配

〇、C语言的动态内存分配方式 关于C语言的动态内存分配方式&#xff0c;简单来讲就是使用四个库函数&#xff1a;malloc、calloc、 realloc、free对堆区的内存进行灵活的分配和回收。有兴趣的话可以看看这篇文章: 「C语言进阶1」动态内存分配 一、C的动态内存分配方式 1. 什么…

国产大模型各自优势如何?大家都怎么选?

近日&#xff0c;一份国产大模型排行榜在网上流传&#xff0c;显示文心一言、通义千问、Kimi位居前三名&#xff0c;其中文心一言用户数为2亿&#xff0c;与其他产品拉开明显差距&#xff1b;前三名月访问量总计超过3000万。国内大模型也开始出现分化效应。

Linux部署MySQL

部署MySQL 先停掉虚拟机中的MySQL&#xff0c;确保你的虚拟机已经安装Docker&#xff0c;且网络开通的情况下&#xff0c;执行下面命令即可安装MySQL&#xff1a; docker run -d \ --name mysql \ -p 3306:3306 \ -e TZAsia/Shanghai \ -e MYSQL_ROOT_PASSWORD123 \ mysql 安…

Linux 安装 Docker +Docker Compose + cucker/get_command_4_run_container

TIP&#xff1a;下面演示的 Linux 系统为 CentOS 7.9。 Docker 更新你的系统并安装必要的依赖项&#xff1a; sudo yum update -y sudo yum install -y yum-utils device-mapper-persistent-data lvm2添加 Docker 的官方仓库&#xff1a; sudo yum-config-manager --add-rep…

如何在职场中有效管理时间和任务?好用的待办事项提醒软件

身在职场&#xff0c;时间管理和任务安排是每个人都必须面对的挑战。有效的时间管理不仅能提高工作效率&#xff0c;还能让我们在繁忙的工作中保持清醒的头脑。那么&#xff0c;如何在职场中有效管理时间和任务呢&#xff1f; 制定一个清晰的工作计划是非常必要的&#xff0c;…

springcloud - ribbon 饥饿加载

一、未饥饿加载前 我们的服务者端口是8081&#xff0c;消费者端口是8085 当我们将两个项目都启动的时候&#xff0c;在消费者里日志级别设置未debug&#xff0c;发现找不到8081 二、开启饥饿加载 # 配置饥饿加载,d1为服务名 ribbon.eager-load.enabledtrue ribbon.eager-loa…

Echarts X轴类目名太长时隐藏显示全部

echarts图表X轴 在柱状图中,X轴类目名如果数据太长; echarts会默认进行隐藏部分字段; 如果我们想让每一个类目名都显示出来,需要进行额外的处理X轴类目名太长时,默认只显示一部分类目名 <!DOCTYPE html> <html lang="en"> <head><meta ch…

基于51单片机的超声波测距及温度显示

基于51单片机的超声波测距 &#xff08;仿真&#xff0b;程序&#xff0b;PCB原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.超声波测距传感器HC-SR04、温度传感器DS18B20将检测的数据传给51单片机&#xff1b; 2.LCD1602实时显示测得的距离和温…