03-JAVA设计模式-观察者模式

观察者模式

什么是观察者模式

Java中的观察者模式是一种常见的设计模式,它允许对象(观察者)订阅另一个对象(被观察者)的状态变化,并在状态变化时自动得到通知。

核心:
观察者模式主要用于1:N的通知。当一个对象(目标对象)的状态发生变化时,它需要及时告知一系列对象(观察者对象)令他们做出响应。

通知观察者的方式:

  • 推:
    • 每次都会把通知以广播的方式发送给所有观察者,所有观察者只能被动接收。
  • 拉:
    • 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容都可以自主决定。

优点:

  • 解耦:观察者模式降低了对象之间的耦合度,使得它们可以独立地改变和复用。
  • 扩展性:新的观察者可以很容易地加入到现有系统中,而无需修改其他部分的代码。
  • 灵活性:观察者模式允许在运行时动态地改变观察者和被观察者的关系。

缺点:

  • 开发和调试复杂:由于系统中存在多个观察者和被观察者,开发和调试可能会变得相对复杂。
  • 性能问题:如果观察者数量庞大,或者被观察者状态改变频繁,可能会导致性能下降。
  • 消息通知顺序:在Java中,消息的通知一般是顺序执行的,一个观察者卡顿可能会影响整体的执行效率。

常见场景:

  • 图形用户界面(GUI)开发:在GUI应用程序中,当用户与界面进行交互(例如点击按钮或拖动滑块)时,界面的某些部分可能需要更新以反映这些变化。观察者模式允许这些部分(观察者)自动响应这些事件,而无需手动调用更新方法。
  • 游戏开发:在游戏中,玩家和NPC的行为,如移动、攻击、受伤等,都可能触发一系列事件。观察者模式可以用来注册和通知这些事件的监听器,以便游戏逻辑能够实时响应并更新游戏状态。
  • 消息传递系统:在分布式系统或微服务架构中,组件之间需要传递消息以实现通信和协作。观察者模式允许一个组件(被观察者)在产生消息时通知所有订阅了该消息的组件(观察者),从而实现高效的消息传递。
  • 实时数据监控:在需要实时跟踪和响应数据变化的场景中,如股票价格监控、传感器数据读取等,观察者模式可以帮助实现当数据变化时自动触发相应的处理逻辑。
  • 状态管理:当应用程序中某个对象的状态发生变化,并且需要通知其他对象进行相应的处理时,可以使用观察者模式。例如,一个订单状态的改变可能需要通知库存系统、支付系统等多个系统进行相应的操作。

案例1

推方式实现,以广播方式发送消息所有观察者

UML

在这里插入图片描述

实现步骤:

  • 创建观察者接口,定义通过被观察者更新状态接口
  • 创建被观察者,被观察者持有所有观察者的引用,提供注册、移除、通知所有观察者的方法,定义状态属性,提供get/set方法set方法中默认通知所有观察者

实现代码

Observer.java

// 观察者接口
public interface Observer {void update(Subject subject);
}

Subject.java

import java.util.ArrayList;
import java.util.List;// 被观察者
public class Subject {// 定义存储观察者的集合private List<Observer> observerList = new ArrayList<Observer>();// 定义状态属性private String state;// 注册观察者public void registerObserver(Observer observer){observerList.add(observer);}// 移除观察者public void removeObserver(Observer observer){observerList.remove(observer);}// 通知所有观察者public void notifyObservers(){for(Observer observer : observerList){observer.update(this);}}public String getState() {return state;}public void setState(String state) {this.state = state;notifyObservers();}
}

ObserverA.java

// 观察者A
public class ObserverA implements Observer{@Overridepublic void update(Subject subject) {System.out.printf("观察者A接收到消息-state:%s%n",subject.getState());}
}

ObserverB.java

// 观察者B
public class ObserverB implements Observer{@Overridepublic void update(Subject subject) {System.out.printf("观察者B接收到消息-state:%s%n",subject.getState());}
}

TestClient.java

public class TestClient {public static void main(String[] args) {Subject subject = new Subject();subject.registerObserver(new ObserverA());subject.registerObserver(new ObserverB());subject.setState("START");}
}

执行结果:

在这里插入图片描述

案例2

使用java.util提供的Observable,Observer类实现案例1。

该方式已在1.9版本中移除,不再推荐使用

实现代码

ConcreteSubject.java

import java.util.Observable;// 目标对象
public class ConcreteSubject extends Observable {private String state;public void setState(String state) {this.state = state;// 表示目标对象已被改变setChanged();// 通知所有观察者notifyObservers();}public String getState() {return state;}
}

ObserverA.java

import java.util.Observable;
import java.util.Observer;/*** 观察者A** @author Anna.* @date 2024/4/25 16:15*/
public class ObserverA implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.printf("观察者A接收到消息-state:%s%n", ((ConcreteSubject) o).getState());}
}

ObserverB.java

import java.util.Observable;
import java.util.Observer;// 观察者B
public class ObserverB implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.printf("观察者B接收到消息-state:%s%n", ((ConcreteSubject) o).getState());}
}

TestClient.java

public class TestClient {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();subject.addObserver(new ObserverA());subject.addObserver(new ObserverB());subject.setState("STOP");}
}

执行结果:
在这里插入图片描述

案例3

通过拉方式,实现观察者主动拉取被观察者当前状态

UML

在这里插入图片描述

实现步骤:

  • 创建被观察者Subject,定义状态属性并提供get/set方法
  • 定义观察者接口类,提供拉取状态接口
  • 创建具体的观察者,实现观察者接口,通过持有被观察者的引用,主动拉取被观察者当前状态

实现代码

Subject.java

// 被观察者
public class Subject {// 定义当前状态private String state;public String getState() {return state;}public void setState(String state) {this.state = state;}
}

Observer.java

// 观察者接口
public interface Observer {// 定义拉取信息的接口void update();
}

ConcreteObserver.java

// 观察者具体实现
public class ConcreteObserver implements Observer {// 定义被观察者,持有被观察者的引用private Subject subject;public ConcreteObserver(Subject subject) {this.subject = subject;}@Overridepublic void update() {System.out.printf("观察者主动拉取状态-state:%s%n ", subject.getState());}
}

TestClient.java

public class TestClient {public static void main(String[] args) {// 创建被观察者Subject subject = new Subject();// 创建观察者ConcreteObserver observer = new ConcreteObserver(subject);// 修改状态subject.setState("RUN");// 观察者主动拉取observer.update();}
}

执行结果:

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

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

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

相关文章

手搓带头双向循环链表(C语言)

目录 List.h List.c ListTest.c 测试示例 带头双向循环链表优劣分析 List.h #pragma once#include <stdio.h> #include <stdlib.h> #include <assert.h>typedef int LTDataType;typedef struct ListNode {struct ListNode* prev;struct ListNode* next…

如何提升WordPress网站安全

上周遇到Hostease的客户反馈他想要提升wordpress网站的安全性。提升WordPress网站安全是网站所有者必须重视的事项。以下是一些有效的安全措施&#xff0c;可帮助您保护WordPress网站免受潜在威胁&#xff1a; 1.选择可靠的WordPress主机 选择一个可靠的WordPress主机提供商至…

关于文档中心的英文快捷替换方案

背景&#xff1a;文档中心需要接入国际化&#xff0c;想节省时间做统一英文方案处理&#xff1b; 文档中心是基于vuepress框架编写的&#xff1b; 1、利用百度翻译 API 的接口去做底层翻译处理&#xff0c;https://api.fanyi.baidu.com/需要在该平台上注册账号&#xff0c;个人…

unittest自动化测试框架详解

一、单元测试的定义 1. 什么是单元测试&#xff1f; ​ 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&#xff0c;这里的最小可测试单元通常是指函数或者类&#xff0c;一般是开发来做的&#xff0c;按照测试阶段来…

day5 c++

#include <iostream> using namespace std; class Person { public:string name;int *age;//Person():name(name),age(new int(100)){cout<<"无参构造"<<endl;}Person(string name,int age):name(name),age(new int(100)){cout <<"P的有…

创新科技赋能旅游服务:智慧文旅引领旅游发展新篇章,智能体验助力产业转型升级

随着科技的飞速发展和人们生活水平的提高&#xff0c;旅游业正迎来前所未有的发展机遇。创新科技在旅游服务领域的广泛应用&#xff0c;不仅提升了旅游体验的品质&#xff0c;也为旅游产业的转型升级注入了新的动力。智慧文旅作为旅游业与信息技术深度融合的产物&#xff0c;正…

Linux内核广泛采用的侵入式数据结构设计

Linux内核广泛采用的侵入式数据结构设计恐怕很难应用到一般程序开发中。基本上是个高维十字链表&#xff0c;一个节点(struct)可以同时位于多个hash/list/tree中。我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪…

如何安全高效地进行网点文件下发?

随着IT技术的飞速发展&#xff0c;以银行为代表的企业数字化技术转型带来了大量的电子化文档传输需求。文件传输数量呈几何级数增长&#xff0c;传统集中式文件传输模式在爆炸式的增长需求下&#xff0c;银行网点文件下发的效率、可靠性、安全性等方面&#xff0c;都需要重点关…

工具:如何在国内高速下载SRA

如何在国内高速下载SRA 下载公共测序数据&#xff0c;一般是通过在NCBI上搜索到study的Run SRA号&#xff0c;然后使用NCBI提供的prefetch下载数据&#xff0c;但因NCBI国内访问较为缓慢导致下载速度巨慢&#xff0c;因此推荐使用EBI提供的enaBrowserToolsAspera的方式从ENA下…

艾体宝案例 | 使用Redis和Spring Ai构建rag应用程序

随着AI技术的不断进步&#xff0c;开发者面临着如何有效利用现有工具和技术来加速开发过程的挑战。Redis与Spring AI的结合为Java开发者提供了一个强大的平台&#xff0c;以便快速构建并部署响应式AI应用。探索这一整合如何通过简化的开发流程&#xff0c;让开发者能够更专注于…

大核注意力 LKA | Visual Attention Network

论文名称&#xff1a;《Visual Attention Network》 论文地址&#xff1a;2202.09741 (arxiv.org) 尽管最初是为自然语言处理任务而设计的&#xff0c;但自注意力机制最近在各个计算机视觉领域迅速崭露头角。然而&#xff0c;图像的二维特性给计算机视觉中的自注意力应用带来了…

【Vue】组件化编程

定义 实现应用中局部功能代码和资源的集合 为什么要用组件化编程? 传统方式编写:依赖关系混乱,不好维护,且代码复用率不高 模块化编写:只关注解决js,复用js,简化js的编写与效率 组件方式编写:好维护、复用率更高、提高运行效率 在组件出现之前,我们开发基本都是用htm…

GD32E103C8T6 封装LQFP-48 GigaDevice(兆易创新) 单片机

GD32E103C8T6 是由GigaDevice&#xff08;兆易创新&#xff09;公司生产的一款基于ARM Cortex-M4内核的32位MCU&#xff08;微控制器&#xff09;。以下是GD32E103C8T6的一些主要功能和参数介绍&#xff1a; 主要功能&#xff1a; 高性能ARM Cortex-M4内核: 采用120MHz的ARM …

修复所有 bug 并不能解决所有问题

原文&#xff1a;jeffpsherman - 2024.04.08 在软件领域&#xff0c;如同在制造业&#xff0c;有些问题是由于 bug 或“特殊原因”引发的&#xff0c;而有些则是“常见原因”&#xff0c;这是由于系统设计和实现的性质所导致的。修复 bug 就是移除特殊原因&#xff0c;消除 bu…

基于SpringBoot的合家云社区物业管理平台 - 项目介绍

合家云社区物业管理平台 2.合家云需求&设计 2.1 项目概述 2.1.1 项目介绍 合家云社区物业管理平台是一个全新的 ”智慧物业解决方案“&#xff0c;是一款互联网的专业社区物业管理系统。平台通过社区资产管理、小区管理、访客管理、在线报修、意见投诉等多种功能模块&a…

指针笔试题模拟

题目一 int main() {int a[5] { 1, 2, 3, 4, 5 };int *ptr (int *)(&a 1);printf( "%d,%d", *(a 1), *(ptr - 1));return 0; } 1*&#xff08;a1&#xff09;可以等价于a[1],即第一个打印的是数组第二个下标的元素&#xff1a;2 2 而ptr拿到的是整个数组的地…

C语言之详细讲解文件操作

什么是文件 与普通文件载体不同&#xff0c;文件是以硬盘为载体存储在计算机上的信息集合&#xff0c;文件可以是文本文档、图片、程序等等。文件通常具有点三个字母的文件扩展名&#xff0c;用于指示文件类型&#xff08;例如&#xff0c;图片文件常常以KPEG格式保存并且文件…

第5篇:创建Nios II工程之Hello_World<四>

Q&#xff1a;最后我们在DE2-115开发板上演示运行Hello_World程序。 A&#xff1a;先烧录编译Quartus硬件工程时生成的.sof文件&#xff0c;在FPGA上成功配置Nios II系统&#xff1b;然后在Nios II Eclipse窗口右键点击工程名hello_world&#xff0c;选择Run As-->Nios II …

C#利用IDbCommand实现通用数据库脚本执行程序

目录 关于 ExecuteNonQuery 数据库脚本程序的执行流程 范例运行环境 通用类的设计 引用 DAL类 实现代码 小结 关于 ExecuteNonQuery 在.net 应用中&#xff0c;在数据库中执行脚本程序是经常用到的功能&#xff0c;如数据操作&#xff08;新增、修改、删除等&#xff0…

C语言趣味代码(四)

这一篇主要编写几个打字练习的小程序&#xff0c;然后通过这些小程序的实现来回顾复习我们之前学过的知识&#xff0c;然后通过这写打字练习的小程序来提升我们的打字技术和编程技术。 1. 打字练习 1.1 基本打字练习 1.1.1 基本实现 首先我们来制作一个用于计算并显示输入一…