行为设计模式 -观察者模式- JAVA

news2024/11/27 12:36:43

观察者模式

    • 一.简介
    • 二. 案例
      • 2.1 抽象主题(Subject)
      • 2.2 具体主题(Concrete Subject)
      • 2.3 抽象观察者(Observer)
      • 2.4 具体观察者(Concrete Observer)
      • 2.5 测试
    • 三. 结论
      • 3.1 优缺点
      • 3.2 使用场景

前言

这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。

作者:神的孩子都在歌唱

一.简介

百度百科: 观察者模式是一种对象行为模式。又被称为发布-订阅(Publish/Subscribe)模式, 它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。

个人理解: 这个模式在日常开发中很常见,比如a服务想要b服务磁盘满的时候通知它,那么它只需要把自己的grpc或者http接口给到b服务,b服务订阅后,如果磁盘满了就调用a服务注册的接口 通知。

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

  • 抽象主题(Subject) 它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  • 具体主题(Concrete Subject): 将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
  • 抽象观察者(Observer): 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体观察者(Concrete Observer): 实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

二. 案例

我将实现一个简单的主题(Subject)观察者(Observer)可以注册到该主题。每当有新消息发布到主题时,所有注册观察者都会收到通知,并且他们可以使用该消息。

请添加图片描述

这里是基本Subject接口,它定义了任何具体主题要实现的接口方法。

2.1 抽象主题(Subject)

/**
 * @author chenyunzhi
 * @date 2024/6/5 17:13
 * @Description 主题
 */
public interface Subject {

    /**
     * 注册观察者
     */
    void register(Observer observer);

    /**
     * 取消观察者
     */
    void unregister(Observer observer);

    /**
     * 通知观察者消息有更新
     */
    void notifyObservers();

}

2.2 具体主题(Concrete Subject)

/**
 * @author chenyunzhi
 * @date 2024/6/5 17:21
 * @Description 实现主题
 */
public class SubjectImpl implements Subject{

    /**
     * 同步锁
     */
    private final Object SUB= new Object();

    /**
     * 存储注册的观察者
     */
    private final List<Observer> observers = new ArrayList<>();

    /**
     * 要通知的消息
     */
    private String message;

    /**
     * 防止 notifyObservers方法被外部调用发送错误通知
     */
    private boolean flag;

    @Override
    public void register(Observer observer) {
        if (observer != null) {
            synchronized (SUB) {
                // 如果不在就存储
                if (!observers.contains(observer)) {
                    observers.add(observer);
                }
            }
        }
    }

    @Override
    public void unregister(Observer observer) {
        if (observer != null) {
            synchronized (SUB) {
                observers.remove(observer);
            }
        }
    }

    @Override
    public void notifyObservers() {

        List<Observer> objects = new ArrayList<>();
        //使用同步方法确保通知仅发送给新消息前注册的观察者
        synchronized (SUB) {
            if (!flag) {
                return;
            }
            objects = this.observers;
            this.flag = false;
        }
        for (Observer observer:objects) {
            // 通知观察者,有消息更新
            observer.update(this.message);
        }

    }

    /**
     * 自定义一个消息变更方法方便测试
     */
    public void updateMessage(String message) {
        System.out.println("消息有变更,通知注册的观察者");
        this.message = message;
        this.flag = true;
        // 通知
        notifyObservers();
    }
}

2.3 抽象观察者(Observer)

/**
 * @author chenyunzhi
 * @date 2024/6/5 17:14
 * @Description 观察者类
 */
public interface Observer {

    /**
     * 定义要更新的方法,由主题调用
     */
    void update(String msg);
}

2.4 具体观察者(Concrete Observer)

/**
 * @author chenyunzhi
 * @date 2024/6/5 17:59
 * @Description 观察者实现类
 */
public class ObserverImpl implements Observer{

    /**
     * 观察者名称
     */
    private final String observerName;
    public ObserverImpl(String name) {
        this.observerName = name;
    }

    @Override
    public void update(String msg) {
        System.out.println(observerName + "接收到消息:" + msg);
    }
}

2.5 测试

/**
 * @author chenyunzhi
 * @date 2024/6/6 14:57
 * @Description 观察者模式测试
 */
public class ObserverPatternTest {
    public static void main(String[] args) {
        // 创建观察者并注册到主题
        SubjectImpl subject = new SubjectImpl();
        subject.register(new ObserverImpl("观察者1"));
        subject.register(new ObserverImpl("观察者2"));
        subject.register(new ObserverImpl("观察者3"));

        // 测试 更新消息
        subject.updateMessage("订阅的主题有消息更新了");

    }
}

请添加图片描述

三. 结论

3.1 优缺点

1.优点:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】

2.缺点:

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

3.2 使用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时。

作者:神的孩子都在歌唱

本人博客:https://blog.csdn.net/weixin_46654114

转载说明:务必注明来源,附带本人博客连接。

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

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

相关文章

从零开始讲PCIe(0)——外设与外设总线

一、外设 计算机外设&#xff08;Peripheral&#xff09;是指连接到计算机主机以扩展其功能的外部设备。这些设备可以是输入设备、输出设备、存储设备或通信设备等&#xff0c;外设&#xff08;外围设备&#xff09;通过输入、输出、存储和通信等方式帮助计算机与用户和其他设备…

用Arduino单片机读取PCF8591模数转换器的模拟量并转化为数字输出

PCF8591是一款单芯片&#xff0c;单电源和低功耗8位CMOS数据采集设备。博文[1]对该产品已有介绍&#xff0c;此处不再赘述。但该博文是使用NVIDIA Jetson nano运行python读取输入PCF8591的模拟量的&#xff0c;读取的结果显示在屏幕上&#xff0c;或输出模拟量点亮灯。NVIDIA J…

可解释聚类又“炸出圈”啦!把准3个切入点一路开挂!创新思路一学就会~

可解释聚类是机器学习领域一个非常重要的研究方向&#xff0c;它通过引入解释性强的特征和模型&#xff0c;让我们更直观地理解聚类结果&#xff0c;从而提升聚类分析的准确性和可靠性。 这种方法在一些敏感领域如医疗、金融等非常适用&#xff0c;因为它与传统方法不同&#…

工具模块及项目整体模块框架

文章目录 工具模块logger.hpphelper.hppthreadpool.hpp 核心概念核心API交换机类型持久化⽹络通信消息应答持久化数据管理中心模块虚拟机管理模块交换路由模块消费者管理模块信道管理模块连接管理模块Broker服务器模块消费者管理信道请求模块通信连接模块项⽬模块关系图 工具模…

Oracle SQL语句没有过滤条件,究竟是否会走索引??

答案是&#xff1a;可能走索引也可能不走索引&#xff0c;具体要看列的值可不可为null&#xff0c;Oracle不会为所有列的nullable属性都为Y的sql语句走索引。 例子&#xff1a; create table t as select * from dba_objects; CREATE INDEX ix_t_name ON t(object_id, objec…

MySQL 中的 GTID 复制详解

MySQL 中的 GTID 复制详解 在 MySQL 的复制架构中&#xff0c;GTID&#xff08;Global Transaction Identifier&#xff09;复制是一种重要的技术&#xff0c;它为数据库的复制提供了更强大的功能和更高的可靠性。本文将深入探讨 MySQL 中的 GTID 复制是什么&#xff0c;以及它…

OpenCV计算机视觉库

计算机视觉和图像处理 Tensorflow入门深度神经网络图像分类目标检测图像分割OpenCVPytorchNLP自然语言处理 OpenCV 一、OpenCV简介1.1 简介1.2 OpenCV部署1.3 OpenCV模块 二、OpenCV基本操作2.1 图像的基本操作2.1.1 图像的IO操作2.1.2 绘制几何图像2.1.3 获取并修改图像的像素…

时间相关数据的统计分析(笔记更新中)

对事件相关数据的统计思路做一个笔记 可以用作肿瘤生长曲线&#xff08;Tumor Growth Curve&#xff09;/某一个药物处理后不同时间点表型的获取类型的数据。 总体来说合适的有两类&#xff0c;一类是以ANOVA为基础的方差分析&#xff0c;重复测量资料的方差分析&#xff1b;…

D - Connect the Dots Codeforces Round 976 (Div. 2)

原题 D - Connect the Dots 思路 直接去做的话会超时, 因此用差分去优化 代码 #include <bits/stdc.h> using namespace std;int f[200020]; int z; int b[11][200020];// 并查集的 find 函数 int find(int x) {return f[x] ! x ? f[x] find(f[x]) : x; }// 检查是…

食品饮料小程序搭建私域会员管理

食品饮料是商超主要经营类目之一&#xff0c;多样化的品牌/厂商/渠道/经销商&#xff0c;客户在消费方面购物渠道和选择范围广&#xff0c;无论厂商还是线下门店/线上电商都需要围绕流量/会员开展生意获得更多营收。 小程序开店基于微信平台生态分享宣传、用户店铺方便购物及提…

Flutter与原生代码通信

文章目录 1. 知识回顾2. 示例代码3. 经验总结我们在上一章回中介绍了通道相关的内容,本章回中将介绍其中的一种通道:MethodChannnel.闲话休提,让我们一起Talk Flutter吧。 1. 知识回顾 我们在上一章回中介绍了通道的概念和作用,并且提到了通道有不同的类型,本章回将其中一…

【C++】类与对象基础概念解析

恭喜你学习完C语言与数据结构的有关内容&#xff0c;现在让我们开始进行对C的学习吧~ &#x1f49d;&#x1f49d;&#x1f49d;如果你对C语言或数据结构还存在疑惑&#xff0c;欢迎观看我之前的作品 &#x1f449;【数据结构】 &#x1f449;【C语言】 目录 一、引言 二、类…

【2024年最新】基于springboot+mysql就业信息管理系统

技术摘要 技术框架&#xff1a;以springboot作为框架&#xff0c;业务模式&#xff1a;B/S模式数据库&#xff1a;MySql作为后台运行的数据库服务器&#xff1a;使用Tomcat用为系统的服务器 系统展示 系统实现功能 本次实现一个就业信息管理系统&#xff0c;通过这个系统能够…

vscode安装及c++配置编译

1、VScode下载 VS Code官网下载地址&#xff1a;Visual Studio Code - Code Editing. Redefined。 2、安装中文插件 搜索chinese&#xff0c;点击install下载安装中文插件。 3、VS Code配置C/C开发环境 3.1、MinGW-w64下载 VS Code是一个高级的编辑器&#xff0c;只能用来写代…

嵌入式系统中qt开发 Qdebug输出中文的时候变成了问号 ??? bulideroot制作的根文件系统

嵌入式系统中qt开发 Qdebug输出&#xff1f;&#xff1f;&#xff1f; bulideroot制作的根文件系统 这个问题我找了三四天了&#xff0c;因为的字符也配置了 /etc/profile中qt的环境变量我也配置了 我的/usr/share/fonts也是有字库的&#xff0c;但是qt输出的中文全是&#…

windows 11 LTSC 26100.1742 官方简体中文版

系统简介 Windows 11 LTSC&#xff08;长期服务通道&#xff09;是一个专为长期稳定性和可靠性设计的Windows 11变体&#xff0c;适合于需要最小更新和更改的关键任务系统和设备。与常规版本相比&#xff0c;LTSC版本的特点是更新频率较低&#xff0c;目的是为了保持系统的稳定…

从零开始掌握YOLOv11:揭秘三大损失函数的理想值(源码+实战)

相关文章&#xff1a; YOLOv1–v11: 版本演进及其关键技术解析-CSDN博客 YOLOv11&#xff1a;重新定义实时目标检测的未来-CSDN博客 Yolo v11目标检测实战1&#xff1a;对象分割和人流跟踪&#xff08;附源码&#xff09;-CSDN博客 YOLOv11目标检测实战2&#xff1a;人流统计…

win10下cuda12.1 +troch2.4.1+vs2022环境下编译安装flash-attn

步骤一 下载项目 先下载 https://github.com/Dao-AILab/flash-attention&#xff0c;然后在conda环境中进入项目目录 步骤二 安装依赖项 执行以下命令&#xff0c;安装cutlass库&#xff0c;该库为编译flash-attn的必须依赖 conda update --force conda conda install conda…

Linux文件重定向文件缓冲区

目录 一、C文件接口 二、系统文件I/O 2.1认识系统文件I/O 2.2系统文件I/O 2.3系统调用和库函数 2.4open( )的返回值--文件描述符 2.5访问文件的本质 三、文件重定向 3.1认识文件重定向 3.2文件重定向的本质 3.3在shell中添加重定向功能 3.4stdout和stderr 3.5如何理…

Java | Leetcode Java题解之第446题等差数列划分II-子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int numberOfArithmeticSlices(int[] nums) {int ans 0;int n nums.length;Map<Long, Integer>[] f new Map[n];for (int i 0; i < n; i) {f[i] new HashMap<Long, Integer>();}for (int i 0;…