观察者模式和订阅模式

news2024/11/24 11:49:17

观察者模式和订阅模式在概念上是相似的,它们都涉及到一个对象(通常称为“主题”或“发布者”)和多个依赖对象(称为“观察者”或“订阅者”)之间的关系。然而,尽管它们有相似之处,但在某些方面也存在细微的差别。

观察者模式(Observer Pattern)

‌核心思想‌

定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

‌结构‌

通常包括主题(Subject)和观察者(Observer)两个主要角色。主题维护一个观察者列表,当状态变化时,通知列表中的所有观察者。

‌实现方式‌

观察者模式可以通过在主题中维护一个观察者列表,并提供注册(addObserver)和注销(removeObserver)观察者的方法来实现。当主题状态变化时,遍历观察者列表并调用每个观察者的更新方法。

‌应用场景‌

常用于事件处理系统、GUI工具包中的事件监听器、订阅-发布系统等。

Demo

设计一个天气预报系统,当天气变化时,通知多个订阅了天气预报的用户。

定义 Subject 接口

Subject接口,被观察者,代表被观察的对象,定义注册新观察者,移除观察者,和通知观察者三个接口。

package org.example.observer;

import java.util.Observer;

public interface MySubject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

实现 Subject 接口

储存观察者列表,被观察对象变化信息,通知观察者列表

package org.example.observer;

import java.util.ArrayList;
import java.util.List;

public class WeatherStation implements MySubject {
    private List<MyObserver> observers;
    private String weather;

    public WeatherStation() {
        observers = new ArrayList<MyObserver>();
    }

    @Override
    public void registerObserver(MyObserver o) {
        this.observers.add(o);
    }

    @Override
    public void removeObserver(MyObserver o) {
        this.observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        this.observers.forEach(observer -> observer.update(weather));
    }

    public void setWeather(String weather) {
        this.weather = weather;
        notifyObservers();
    }
}

定义 Observer 接口

Observer接口,代表观察者,定义接收到通知后需要执行的动作接口

package org.example.observer;

public interface MyObserver {
    void update(String weather);
}

实现 Observer 接口

观察者接收到通知后,实现具体要执行的动作

package org.example.observer;

public class User implements MyObserver {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String weather) {
        System.out.println(String.format("name %s receive weather update : %s", name, weather));
    }
}

测试

package org.example.observer;

public class MyObserverMain {
    public static void main(String[] args) {
        // 创建被观察者对象
        WeatherStation station = new WeatherStation();

        // 创建观察者对象
        User userA = new User("A");
        User userB = new User("B");
        User userC = new User("C");

        // 注册观察者,并更新天气
        station.registerObserver(userA);
        station.registerObserver(userB);
        station.registerObserver(userC);
        station.setWeather("Sunny");

        // 移除部分观察者,再次更新天气
        station.removeObserver(userA);
        station.setWeather("Rainy");

    }
}

订阅模式(Subscription Pattern)

‌核心思想‌

也是一种一对多的关系,但更强调“订阅”的概念。订阅者订阅某个主题或频道,以接收该主题或频道发布的更新或消息。

‌结构‌

通常包括发布者(Publisher)、订阅者(Subscriber)和消息(Message)三个主要角色。订阅者通过订阅某个发布者来接收其发布的消息。

‌实现方式‌

订阅模式可以通过事件总线(Event Bus)、消息队列(Message Queue)或专门的订阅系统来实现。订阅者可以订阅特定的主题或频道,并接收该主题或频道发布的消息。

‌应用场景‌

广泛用于消息传递系统、事件驱动架构、分布式系统中的服务间通信等。

Demo

定义消息类

package org.example.publish_subscriber;

public class Message {
    private String content;

    public Message(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

定义订阅者接口

package org.example.publish_subscriber;

public interface Subscriber {
    void receive(String message);
}

实现订阅者接口

package org.example.publish_subscriber;

public class User implements Subscriber{
    private String name;
    
    public User(String name) {
        this.name = name;
    }
    
    @Override
    public void receive(String message) {
        System.out.println(String.format("%s received message: %s", name, message));
    }
}

定义事件总线

package org.example.publish_subscriber;

import java.util.Map;
import java.util.concurrent.*;

public class EventBus {
    private final Map<Subscriber, BlockingQueue<Message>> subscriberQueues = new ConcurrentHashMap<>();
    private final ExecutorService executor = Executors.newCachedThreadPool();
    private volatile boolean running = true;

    public void subscriber(Subscriber subscriber) {
        BlockingQueue<Message> queue = new LinkedBlockingQueue<>();
        subscriberQueues.put(subscriber, queue);
        executor.submit(() -> {
            try {
                while (running || !queue.isEmpty()) {
                    Message message = queue.take();
                    subscriber.receive(message);
                }
            } catch (Exception e) {
                Thread.currentThread().interrupt();
            }
        });
    }

    public void publish(Message message) {
        subscriberQueues.values().forEach(queue -> {
            try {
                queue.put(message);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }

    public void shutdown() {
        running = false;
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

测试

package org.example.publish_subscriber;

public class PubSubMain {
    public static void main(String[] args) throws InterruptedException {
        EventBus eventBus = new EventBus();

        User userA = new User("A");
        User userB = new User("B");
        User userC = new User("C");
        User userD = new User("D");

        eventBus.subscriber(userA);
        eventBus.subscriber(userB);
        eventBus.subscriber(userC);
        eventBus.subscriber(userD);

        for (int i = 0; i < 10; i++) {
            eventBus.publish(new Message(i + "Hello World!"));
            eventBus.publish(new Message(i + "Hello Shore!"));
        }
        Thread.sleep(10000);

        eventBus.shutdown();
    }
}

在上面的例子中,executor.submit 被用于提交订阅者线程的任务。每个订阅者都有一个对应的工作队列,当发布消息时,消息会被放入每个订阅者的工作队列中。订阅者线程会不断地从自己的工作队列中取出消息并处理。通过这种方式,实现了发布-订阅模式中的异步通信和消息分发

区别与联系

‌区别‌

观察者模式更侧重于对象间的依赖关系和状态变化的通知机制;而订阅模式更强调消息的传递和订阅-发布的关系。此外,观察者模式通常是在单个应用或系统内使用;而订阅模式可能涉及跨系统或跨网络的消息传递。

‌联系‌

两者都涉及到一个对象(主题/发布者)和多个依赖对象(观察者/订阅者)之间的关系,且都实现了某种形式的通知机制。在某些情况下,它们可以相互替代或结合使用。例如,在一个分布式系统中,可以使用订阅模式来实现不同服务之间的消息传递,而在服务内部则可以使用观察者模式来实现状态变化的通知和更新。

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

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

相关文章

自主研发,基于PHP+ vue2+element+ laravel8+ mysql5.7+ vscode开发的不良事件管理系统源码,不良事件管理系统源码

不良事件上报系统源码&#xff0c;不良事件管理系统源码&#xff0c;PHP源码 不良事件上报系统通过 “事前的人员知识培训管理和制度落地促进”、“事中的事件上报和跟进处理”、 以及 “事后的原因分析和工作持续优化”&#xff0c;结合预存上百套已正在使用的模板&#xff0…

取电快充协议芯片,支持全协议、内部集成LDO支持从UART串口读取电压电流消息

H004D 是一款支持全协议的受电端诱骗取电协议芯片&#xff0c;支持宽电压输入 3.3V~30V&#xff0c;芯片内部集成LDO&#xff0c;可输出 3.3V电压, 支持 通过UART 串口读取电压电流&#xff0c;支持定制功能&#xff0c;芯片采用QFN_20封装&#xff0c;线路简单&#xff0c;芯片…

科研数据处理工具Graphpad Prism 10.1+9.5下载安装教程

GraphPad Prism 是一个功能强大且易于使用的统计分析和绘图软件&#xff0c;专为生物医学研究设计而开发。 该软件提供了丰富的统计工具和绘图功能&#xff0c;使用者能够轻松进行数据分析和统计检验。它支持一系列的统计方法&#xff0c;包括T检验、方差分析、非参数检验、生…

Flutter:AnimatedIcon图标动画,自定义Icon通过延时Interval,实现交错式动画

配置vsync&#xff0c;需要实现一下with SingleTickerProviderStateMixinclass _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{// late延迟初始化 AnimationControllerlate AnimationController _controller;overridevoid initStat…

【网络系统管理】2023年全国职业院校技能大赛:组策略--10套题组合--1

1、限制访问C盘; (1)搜索《我的电脑》 (2)用户配置\策略\管理模板\Windows组件\文件资源管理器 2、禁止运行run.exe; (1)搜索《应用程序》 (2)用户配置\策略\管理模板\系统

【LeetCode每日一题】——485.最大连续 1 的个数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 数组 二【题目难度】 LeetCode 三【题目编号】 485.最大连续 1 的个数 四【题目描述】 给定…

SpringCloud Gateway转发请求到同一个服务的不同端口

SpringCloud Gateway默认不支持将请求路由到一个服务的多个端口 本文将结合Gateway的处理流程&#xff0c;提供一些解决思路 需求背景 公司有一个IM项目&#xff0c;对外暴露了两个端口8081和8082&#xff0c;8081是springboot启动使用的端口&#xff0c;对外提供一些http接口…

助力3C数码企业实现泛微OA与金蝶EAS的高效对接

助力3C数码企业实现泛微OA与金蝶EAS的高效对接 在3C数码行业&#xff0c;数据的实时性和准确性对于企业的运营至关重要。轻易云数据集成平台&#xff0c;作为业界领先的无代码软件集成解决方案&#xff0c;致力于帮助3C数码企业实现系统间的无缝对接&#xff0c;提升数据管理效…

<OS 有关> ubuntu 24 不同版本介绍 安装 Vmware tools

原因 想用 apt-get download 存到本地 / NAS上&#xff0c;减少网络流浪。 看到 VMware 上的确实有 ubuntu&#xff0c;只是版本是16。 ubuntu 版本比较&#xff1a;LTS vs RR LTS: Long-Term Support 长周期支持&#xff0c; 一般每 2 年更新&#xff0c;会更可靠与更稳定…

python之使用django框架开发web项目

本问将对django框架在python的web项目中的使用进行介绍,有不对之处,烦请指正。 首先使用创建一个django工程(本示例中使用pycharm2024+python3.12),名称和项目保存路径根据自己的需要自行修改,新手直接默认本机环境就好(关于conda将会另开一篇进行讲解。),最后点击cre…

【大数据学习 | Spark-Core】Spark的分区器(HashPartitioner和RangePartitioner)

之前学过的kv类型上面的算子 groupby groupByKey reduceBykey sortBy sortByKey join[cogroup left inner right] shuffle的 mapValues keys values flatMapValues 普通算子&#xff0c;管道形式的算子 shuffle的过程是因为数据产生了打乱重分&#xff0c;分组、排序、join等…

GPT系列文章

GPT系列文章 GPT1 GPT1是由OpenAI公司发表在2018年要早于我们之前介绍的所熟知的BERT系列文章。总结&#xff1a;GPT 是一种半监督学习&#xff0c;采用两阶段任务模型&#xff0c;通过使用无监督的 Pre-training 和有监督的 Fine-tuning 来实现强大的自然语言理解。在 Pre-t…

QT 网络编程 数据库模块 TCP UDP QT5.12.3环境 C++实现

一、网络编程 1. 模块引入 QT network 2. 头文件 #include <QTcpServer> //TCP服务端使用 #include <QTcpSocket> //TCP服务器和客户端都使用 3. TCP网络编程流程 1) 服务端 实例化QTcpServer对象----------------------------->socket 进入监听状态…

Cmakelist.txt之Linux-redis配置

1.cmakelist.txt cmake_minimum_required(VERSION 3.16) ​ project(redis_linux_test LANGUAGES C) ​ ​ ​ add_executable(redis_linux_test main.c) ​ # 设置hiredis库的头文件路径和库文件路径 set(Hiredis_INCLUDE_DIR /usr/local/include/hiredis) set(Hiredis_LIBRA…

使用flink编写WordCount

1. env-准备环境 2. source-加载数据 3. transformation-数据处理转换 4. sink-数据输出 5. execute-执行 流程图&#xff1a; DataStream API开发 //nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/datastream/overview/ 添加依赖 <properties>&l…

uniop触摸屏维修eTOP40系列ETOP40-0050

在现代化的工业与商业环境中&#xff0c;触摸屏设备已成为不可或缺的人机交互界面。UNIOP&#xff0c;作为一个知名的触摸屏品牌&#xff0c;以其高性能、稳定性和用户友好性&#xff0c;广泛应用于各种自动化控制系统、自助服务终端以及高端展示系统中。然而&#xff0c;即便如…

基于AXI PCIE IP的FPGA PCIE卡示意图

创作不易&#xff0c;转载请注明出处&#xff1a;https://blog.csdn.net/csdn_gddf102384398/article/details/143926217 上图中&#xff0c;在FPGA PCIE卡示意图内&#xff0c;有2个AXI Master设备&#xff0c;即&#xff1a;PCIE到AXI4-Full-Master桥、AXI CDMA IP&#xff1…

学习与理解LabVIEW中多列列表框项名和项首字符串属性

多列列表框控件在如下的位置&#xff1a; 可以对该控件右击&#xff0c;如下位置&#xff0c;即可设置该控件的显示项&#xff1a; 垂直线和水平线指的是上图中组成单元格的竖线和横线&#xff08;不包括行首列首&#xff09; 现在介绍该多列列表框的两个属性&#xff0c;分别…

使用 前端技术 创建 QR 码生成器 API1

前言 QR码&#xff08;Quick Response Code&#xff09;是一种二维码&#xff0c;于1994年开发。它能快速存储和识别数据&#xff0c;包含黑白方块图案&#xff0c;常用于扫描获取信息。QR码具有高容错性和快速读取的优点&#xff0c;广泛应用于广告、支付、物流等领域。通过扫…

UE5材质篇5 简易水面

不得不说&#xff0c;UE5里搞一个水面实在是相比要自己写各种反射来说太友好了&#xff0c;就主要是开启一堆开关&#xff0c;lumen相关的&#xff0c;然后稍微连一些蓝图就几乎有了 这里要改一个shading model&#xff0c;要这个 然后要增加一个这个node 并且不需要连接base …