设计模式|观察者模式

news2025/2/23 4:07:18
  • 观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有观察者都会收到通知并更新。观察者模式常用于实现事件处理系统、发布-订阅模式等。
  • 在项目中,我们使用RabbitMQ、Kafka、ActiveMQ等消息中间件来进行解耦、异步、广播、削峰,在设计模式中我们可以使用观察者模式也能达到解耦、异步的特点。下面我将介绍观察者模式。

观察者模式的主要角色

  • Subject(主题):被观察的对象,包含对观察者的引用列表,并提供注册、移除和通知观察者的方法。
  • Observer(观察者):定义一个更新接口,用于接收主题的通知。
  • ConcreteSubject(具体主题):具体的主题,实现了主题接口,状态发生改变时通知所有注册的观察者。
  • ConcreteObserver(具体观察者):具体的观察者,实现了观察者接口,响应主题的变化。

Java 实现观察者模式

下面是一个简单的 Java 观察者模式示例:

1. 定义观察者接口
public interface  Observer {
    void update(String message);// 处理业务逻辑
}
2. 定义主题接口
public interface  Subject {
    void registerObserver(Observer observer);// 添加观察者
    void removeObserver(Observer observer);// 移除观察者
    void notifyObservers();// 通知所有观察者事件
}
3. 实现具体主题
public class ConcreteSubject implements Subject{
    private List<Observer> observers;
    private String message;

    public ConcreteSubject() {
        this.observers = new ArrayList<>();
    }

    @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(message);
        }
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}
4. 实现具体观察者
public class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }
    // 模拟处理业务逻辑
    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}
5.测试
public class ObserverPatternDemo {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");
        Observer observer3 = new ConcreteObserver("Observer 3");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);
        subject.registerObserver(observer3);
        System.out.println("----------------------向三个订阅者发送消息 First Message----------------------");
        subject.setMessage("First Message");
        System.out.println("----------------------向三个订阅者发送消息 Second Message----------------------");
        subject.setMessage("Second Message");
        System.out.println("----------------------依次一个订阅者并发送消息 Third Message-------------------------");
        subject.removeObserver(observer2);
        subject.setMessage("Third Message");
    }
}
6.结果image.png
7.分析
  • 在这里我们可以看到,我们已经可以做到可以向多个订阅者发送消息,而只需要创建一个新的订阅者放入observers容器里面,从而达到解耦的目的。

  • 而要达到异步的功能,可以利用java的并发工具,比如线程、“ExecutorService”等。下面给出实现思路:

1.使用 ExecutorService:

使用 Executors.newCachedThreadPool() 创建一个可缓存的线程池。可以使用其他类型的线程池,如 FixedThreadPool 或 SingleThreadExecutor,根据具体需求选择。

2.异步通知:

在 notifyObservers 方法中,通过 executorService.submit() 提交任务,使得每个观察者的 update 方法在单独的线程中执行,达到了异步通知的效果。

3.资源管理:

在合适的时候调用 executorService.shutdown() 来关闭线程池,确保资源得到释放。通过这种方式,实现了观察者模式的异步通知,使得观察者的 update 方法可以并发执行,提高了效率,避免了通知过程中阻塞的问题。

观察者模式在框架中的应用

1. Java Swing

Java Swing 中的事件处理模型大量使用了观察者模式。按钮点击、窗口关闭等事件的处理都是通过事件监听器实现的。

2. Spring Framework

Spring 框架中也广泛使用了观察者模式,特别是在事件驱动的编程模型中。

  • ApplicationEvent 和 ApplicationListener: Spring 提供了一个事件机制,通过 ApplicationEventApplicationListener 实现观察者模式。
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("Context Refreshed Event received!");
    }
}

public class MyCustomEvent extends ApplicationEvent {
    public MyCustomEvent(Object source) {
        super(source);
    }
}

@Component
public class MyCustomEventListener implements ApplicationListener<MyCustomEvent> {
    @Override
    public void onApplicationEvent(MyCustomEvent event) {
        System.out.println("Custom Event received!");
    }
}

// 发布事件
@Component
public class MyBean {
    private final ApplicationEventPublisher publisher;

    public MyBean(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void publishEvent() {
        MyCustomEvent event = new MyCustomEvent(this);
        publisher.publishEvent(event);
    }
}

3. Google Guava

Google Guava 提供的 EventBus 是一种实现发布-订阅(Pub/Sub)模式的工具,它的实现也是采用设计模式中的观察者设计模式。,它广泛应用于解耦事件生产者和消费者。在 EventBus 中,事件生产者和事件消费者可以彼此独立地工作,彼此无需直接依赖。

观察者模式的优缺点

优点
  1. 解耦:观察者和主题之间是抽象耦合的,可以独立扩展观察者和主题。
  2. 动态订阅:观察者可以在运行时动态添加或移除。
  3. 灵活性:可以在不修改主题类的情况下增加新的观察者,实现了开闭原则。
缺点
  1. 可能引起性能问题:如果观察者较多,通知所有观察者可能会比较耗时。
  2. 可能导致内存泄漏:如果观察者没有及时被移除,可能导致内存泄漏。

使用场景

  1. 事件处理系统:GUI 事件监听,按钮点击事件。
  2. 发布-订阅系统:消息队列,订阅推送服务。
  3. 数据变化通知:数据模型变化通知视图更新,类似于 MVC 模式中的观察者角色

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

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

相关文章

C语言 | Leetcode C语言题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; // 判断是否为完全平方数 bool isPerfectSquare(int x) {int y sqrt(x);return y * y x; }// 判断是否能表示为 4^k*(8m7) bool checkAnswer4(int x) {while (x % 4 0) {x / 4;}return x % 8 7; }int numSquares(int n) {if (isPerfect…

CSA笔记5-局域网yum源配置互联网yum源配置源代码编译安装

局域网yum源配置&#xff1a;建设一个本地网络仓库给本地局域网用户提供下载安装 互联网yum源配置&#xff1a;在线获取最新安装文件的通道 1.局域网web方式yum源配置&#xff1a; 服务器提供yum安装服务&#xff1a; yum install -y httpd 安装并启动httpd 在本地源的基…

Spring AOP(2)原理(代理模式和源码解析)

目录 一、代理模式 二、静态代理 三、动态代理 1、JDK动态代理 &#xff08;1&#xff09;JDK动态代理实现步骤 &#xff08;2&#xff09;定义JDK动态代理类 &#xff08;3&#xff09;代码简单讲解 2、CGLIB动态代理 &#xff08;1&#xff09;CGLIB 动态代理类实现…

RockyLinux 9 PXE Server bios+uefi 自动化部署 RockLinux 8 9

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

Java学习Day14:基础篇4

数组 1.理解数组 数组也是一种类型&#xff0c;引用类型&#xff01; 2.数组的定义 3.数组的初始化 静态&#xff1a; 动态&#xff1a; 不同数据类型数组初始化值&#xff1a; null不可用.equal方法&#xff0c;会报错&#xff01; 4.使用数组 5.foreach循环 6.深入数组 7&am…

绘制拟合联合密度分布

绘制拟合联合密度分布 import numpy as np import matplotlib.pyplot as plt from sklearn.neighbors import KernelDensity# 生成模拟数据 np.random.seed(42) mean [0, 0] cov [[1, 0.5], [0.5, 1]] data np.random.multivariate_normal(mean, cov, size200) a data[:, 0…

基于jeecgboot-vue3的Flowable流程-自定义业务表单流程历史信息显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、对于自定义业务表单的流程历史记录信息做了调整&#xff0c;增加显示自定义业务表单 <el-tab-pane label"表单信息" name"form"><div v-if"customF…

德国云手机:企业移动办公解决方案

在现代商业环境中&#xff0c;移动办公已经成为一种趋势。德国云手机作为一种高效的解决方案&#xff0c;为企业提供了强大的支持。本文将探讨德国云手机如何优化企业的移动办公环境。 一、德国云手机的主要优势 高灵活性 德国云手机具有高度的灵活性&#xff0c;能够根据用户需…

鸿蒙HarmonyOS开发:用户通知服务Noification的详细使用指南

文章目录 一、Notification Kit简介二、能力范围三、业务流程四、通知样式&#xff1a;五、约束限制六、开发步骤6.1、导入模块。6.2、构造NotificationRequest对象&#xff0c;并发布通知。6.2.1、普通文本类型。6.2.2、长文本类型。6.2.3、多行文本类型。 6.3、为通知添加行为…

SpringBoot源码(1)ApplicationContext和BeanFactory

1、调用getBean方法 SpringBootApplication public class SpringBootDemoApplication {public static void main(String[] args) {ConfigurableApplicationContext applicationContext SpringApplication.run(SpringBootDemoApplication.class, args);applicationContext.get…

javaFx 系统最小化托盘(及其避坑)、开机自启动

1、系统最小化托盘 参考资料&#xff1a; https://blog.51cto.com/u_14191/6310480 javafx 设置最小窗口大小 - Java (1) - 芒果文档 注意事项 1.直接使用第一个参考资料中的MinWindow类即可。 #最小化托盘 -- 一般放置在 start(Stage stage)处 MinWindow.getInstance().…

鸿蒙仓颉语言【cryptocj 库】(介绍与SHA、MD5、HMAC摘要算法)

cryptocj 库 介绍 cryptocj 是一个安全的密码库&#xff0c;包括常用的密码算法、常用的密钥生成和签名验证。 该库是对 C 语言的 openSSL 封装的仓颉加密算法 1 提供SHA、MD5、HMAC摘要算法。 前置条件&#xff1a;NA 场景&#xff1a; OHOS&#xff0c; Linux&#xff…

昇思25天学习打卡营第17天 | CycleGAN图像风格迁移互换

通过深入学习CycleGAN模型&#xff0c;我对无监督图像到图像的转换技术有了更深的理解。CycleGAN不仅能在没有成对训练样本的情况下实现域之间的转换&#xff0c;而且在保持内容结构的同时成功转换图像风格&#xff0c;这在许多应用中都非常有用&#xff0c;如艺术风格转换、季…

C++树(二)【直径,中心】

目录&#xff1a; 树的直径&#xff1a; 树的直径的性质&#xff1a; 性质1&#xff1a;直径的端点一定是叶子节点 性质2&#xff1a;任意点的最长链端点一定是直径端点。 性质3&#xff1a;如果一棵树有多条直径,那么它们必然相交&#xff0c;且有极长连…

Linux搭建Kubernetes集群(单Master)【附图文】

文章目录 一、集群环境配置要求二、主机准备三、初始环境准备1.关闭防火墙2.关闭 selinux3.关闭swap4.加载 br_netfilter 模块5.允许iptables转发流量6.设置时间同步 四、安装Docker五、安装kubeadm, kubectl, kubelet六、在Master节点部署集群七、将 node 节点加入集群八、部署…

springcloud rocketmq 新增的消费者组从哪里开始消费

如果新建一个新的消费者组&#xff0c;是否会消费历史消息&#xff0c;导致重复消费&#xff1f; 直接在 console 界面新增消费者组&#xff0c;但是没有办法绑定订阅关系&#xff0c;没有找到入口&#xff0c;在 控制台项目源码 rocketmq-externals 也没有找到可以确定订阅关系…

Windows 11预览补丁KB5040527影响火绒驱动加载的解决办法

7 月 11 日&#xff0c;微软更新Windows 11 预览版本补丁 KB5040527&#xff0c;补丁安装后会影响火绒驱动加载导致火绒安全软件服务异常&#xff0c;补丁相关信息如下&#xff1a; https://blogs.windows.com/windows-insider/2024/07/11/releasing-windows-11-builds-22621-…

微信视频号下载又一工具,免费简单易用!

Res-downloader嗅探资源下载器请收好 官方称支持微信视频号、抖音、快手、小红书等网络资源下载 使用方法 第一步&#xff0c;下载软件 文末扫码 或者搜索关注公众号AIshape 回复 RES 获取 或 自行百度搜索下载 第二步&#xff0c;软件设置 打开软件会弹出接受传入网络链…

CSI-RS在信道中传输的过程

简单介绍CSI-RS信号生成&#xff0c;在信道中传输和接收的过程 1.载波配置 首先需要配置载波相关的参数 系统带宽和子载波间隔 5G NR中&#xff0c;系统带宽和子载波间隔是两个关键参数&#xff0c;共同决定无线资源的分配和使用 系统带宽 5G NR支持广泛的系统带宽&…

【SOC 芯片设计 DFT 学习专栏 -- DFT OCC 与 ATPG的介绍】

请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权&#xff0c;请联系删除 转自&#xff1a; 简矽芯学堂 简矽芯学堂 2024年01月18日 09:00 陕西 文章目录 OCC 介绍Fast ScanFull chip ATPGPartition ATPGHierarchical ATPG OCC 介绍 OCC&am…