观察者设计模式(Observer Design Pattern)[论点:概念、组成角色、相关图示、示例代码、框架中的运用、适用场景]

文章目录

  • 概念
  • 组成角色
  • 相关图示
  • 示例代码
  • 框架中的运用
  • 适用场景

概念

        观察者设计模式(Observer Design Pattern)是一种行为型设计模式,它定义了一种对象间的一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生改变时,通知所有观察者对象,使它们能够自动更新。

组成角色

  1. 主题(Subject):主题是一个抽象类或接口,它定义了添加、删除和通知观察者的方法。
  2. 具体主题(ConcreteSubject):具体主题是主题接口的实现,它包含观察者列表和业务逻辑。当状态发生改变时,具体主题负责通知所有观察者。
  3. 观察者(Observer):观察者是一个抽象类或接口,它定义了一个更新方法,用于接收主题的通知。
  4. 具体观察者(ConcreteObserver):具体观察者是观察者接口的实现,它根据主题的通知来更新自己的状态。

相关图示

在这里插入图片描述

示例代码

        这个代码示例展示了观察者设计模式的基本实现。具体主题ConcreteSubject存储了观察者列表和当前状态,当状态发生改变时,它会通知所有注册的观察者。观察者ConcreteObserverAConcreteObserverB实现了观察者接口Observer,并在更新状态时输出相应的消息。在main方法中,我们创建了一个具体主题和两个具体观察者,将观察者添加到主题中,然后改变主题的状态并观察输出结果。当从主题中移除一个观察者后,再次改变主题状态,可以看到只有剩下的观察者接收到了通知。

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

// 主题接口
interface Subject {
    // 添加观察者
    void addObserver(Observer observer);
    // 移除观察者
    void removeObserver(Observer observer);
    // 通知所有观察者
    void notifyObservers();
}

// 具体主题
class ConcreteSubject implements Subject {
    // 存储观察者的列表
    private List<Observer> observers = new ArrayList<>();
    // 主题的状态
    private String state;

    // 设置状态并通知所有观察者
    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }

    // 添加观察者到列表
    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    // 从列表中移除观察者
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    // 通知所有观察者状态已改变
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
}

// 观察者接口
interface Observer {
    // 更新观察者状态
    void update(String state);
}

// 具体观察者A
class ConcreteObserverA implements Observer {
    // 当收到主题通知时,更新状态
    @Override
    public void update(String state) {
        System.out.println("ConcreteObserverA received new state: " + state);
    }
}

// 具体观察者B
class ConcreteObserverB implements Observer {
    // 当收到主题通知时,更新状态
    @Override
    public void update(String state) {
        System.out.println("ConcreteObserverB received new state: " + state);
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        // 创建具体主题
        ConcreteSubject subject = new ConcreteSubject();
        // 创建具体观察者A和B
        ConcreteObserverA observerA = new ConcreteObserverA();
        ConcreteObserverB observerB = new ConcreteObserverB();

        // 向主题添加观察者A和B
        subject.addObserver(observerA);
        subject.addObserver(observerB);

        // 改变主题状态并通知观察者
        subject.setState("New state 1");
        subject.setState("New state 2");

        // 从主题中移除观察者A
        subject.removeObserver(observerA);

        // 再次改变主题状态并通知观察者
        subject.setState("New state 3");
    }
}

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

框架中的运用

在Spring框架中,观察者模式主要应用在事件处理机制中。Spring监听器主要涉及到以下几个构成部分:

  1. ApplicationEvent(事件):
    ApplicationEvent是Spring中所有事件的基类,它继承自java.util.EventObject。自定义事件需要继承ApplicationEvent。当某个特定操作发生时,可以创建一个自定义的ApplicationEvent实例,并将其发布到整个应用程序。
  2. ApplicationListener(监听器):
    ApplicationListener是一个泛型接口,泛型参数为继承自ApplicationEvent的类。监听器负责监听特定类型的事件。实现ApplicationListener接口的类需要实现onApplicationEvent()方法以处理对应的事件。当一个事件被发布时,关注该事件的监听器会收到通知,并执行onApplicationEvent()方法来处理事件。
  3. ApplicationEventPublisher(事件发布器):
    ApplicationEventPublisher是一个接口,负责发布事件。它提供了publishEvent()方法,用于发布事件到整个应用程序。在Spring中,ApplicationContext实现了ApplicationEventPublisher接口,因此事件发布主要由ApplicationContext管理。
  4. ApplicationEventMulticaster(事件多播器):
    ApplicationEventMulticaster是一个接口,负责将事件分发给关联的监听器。SimpleApplicationEventMulticasterApplicationEventMulticaster的一个默认实现。在Spring中,ApplicationEventMulticaster负责管理所有注册的ApplicationListener。当ApplicationEventPublisher发布事件时,ApplicationEventMulticaster会将事件分发给所有关注该事件的监听器。
  • org.springframework.context.ApplicationEvent:

ApplicationEvent是Spring事件模型的基类,它继承了java.util.EventObject。所有自定义事件需要继承ApplicationEvent类。

package org.springframework.context;

import java.util.EventObject;

public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp;

    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }

    public final long getTimestamp() {
        return this.timestamp;
    }
}

  • org.springframework.context.ApplicationListener:

ApplicationListener是一个泛型接口,它的泛型参数是一个继承自ApplicationEvent的类。它类似于观察者模式中的观察者。实现ApplicationListener接口的类需要实现onApplicationEvent()方法以处理事件。

package org.springframework.context;

import java.util.EventListener;

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

  • org.springframework.context.ApplicationEventPublisher:

ApplicationEventPublisher接口负责发布事件。它类似于观察者模式中的主题。实现该接口的类需要实现publishEvent()方法来发布事件。

package org.springframework.context;

public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
    void publishEvent(Object event);
}

  • org.springframework.context.event.SimpleApplicationEventMulticaster

SimpleApplicationEventMulticasterApplicationEventMulticaster接口的一个实现。它负责将事件分发给关联的监听器。其主要方法是multicastEvent(ApplicationEvent),该方法会遍历注册的监听器,并将事件分发给匹配的监听器。

package org.springframework.context.event;

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    // ...

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            } else {
                invokeListener(listener, event);
            }
        }
    }

    // ...
}
  • org.springframework.context.support.AbstractApplicationContext

AbstractApplicationContext实现了ApplicationEventPublisher接口,因此在Spring中,事件发布主要由ApplicationContext管理。AbstractApplicationContext中的publishEvent(ApplicationEvent)方法会委托给ApplicationEventMulticaster进行事件的发布。

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {

    // ...

    @Override
    public void publishEvent(ApplicationEvent event) {
        publishEvent(event, null);
    }

    @Override
    public void publishEvent(Object event) {
        publishEvent(event, null);
    }

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Publishing event in " + getDisplayName() + ": " + event);
        }

        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        } else {
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
            }
        }

        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        } else {
            getApplicationEventMulticaster().multicastEvent(applicationEvent,
        eventType);
        }
          // 发布事件给父级 ApplicationContext
      	if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            } else {
                this.parent.publishEvent(event);
            }
        }
		}
}
// ...

总之大概的逻辑是这样

  1. 创建自定义事件CustomEvent,该事件需要继承ApplicationEvent

  2. 创建自定义事件监听器CustomEventListener,该监听器需要实现ApplicationListener<CustomEvent>接口。

  3. 在需要的地方使用ApplicationEventPublisher(通常是ApplicationContext)发布事件。在AbstractApplicationContext中,publishEvent方法会将事件委托给ApplicationEventMulticaster进行发布。

  4. SimpleApplicationEventMulticaster(默认的ApplicationEventMulticaster实现)会将事件分发给关联的监听器。在multicastEvent方法中,它会遍历所有注册的监听器,并将事件分发给匹配的监听器。

  5. 监听器收到事件后,将执行onApplicationEvent()方法处理事件。

        通过上述流程,我们可以看到Spring监听器执行原理是基于观察者设计模式的。当事件发布后,所有关注该事件的监听器都会收到通知并执行相应的操作。这种机制实现了松耦合的事件处理,可以在不同组件之间实现动态交互。

适用场景

  1. 事件处理系统:观察者模式可以用于实现事件驱动的架构,当一个事件发生时,所有关心这个事件的观察者都会收到通知。这在GUI开发、游戏开发等场景中非常常见。
  2. 数据绑定和同步:当多个组件需要共享或同步相同的数据时,可以使用观察者模式。当数据发生变化时,所有依赖于该数据的观察者都会收到通知并自动更新。这在前端开发、实时协同编辑等场景中非常有用。
  3. 消息发布和订阅:观察者模式可以用于实现发布/订阅系统。在这种系统中,发布者负责发布消息,订阅者负责订阅消息。当发布者发布新消息时,所有订阅了该消息的订阅者都会收到通知。这在分布式系统、消息队列、事件总线等场景中非常常见。
  4. 状态监控和报警:在系统监控和报警场景中,观察者模式可以用于实时监控被观察对象的状态。当被观察对象的状态发生异常时,观察者可以根据预定义的规则执行相应的操作,如发送报警邮件、记录日志等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 配置文件

前言&#xff1a; 本篇主要介绍两种配置文件格式&#xff0c;分别为properties与yml(yaml)。 需要注意的是&#xff1a; 两个配置文件从功能上来讲是可以同时存在的&#xff0c;但是企业中通常会规定使用某一种格式的配置文件。如果同一个配置出现在两种格式的配置文件中的话&a…

微波方向有哪些SCI期刊推荐? - 易智编译EaseEditing

微波方向的SCI期刊推荐包括&#xff1a; IEEE Transactions on Microwave Theory and Technology&#xff1a; 该期刊是电磁场与微波技术领域的著名期刊&#xff0c;被世界上许多研究机构和大学广泛引用。 IEEE Transactions on Antennas and Propagation&#xff1a; 该期刊…

English Learning - L3 作业打卡 Lesson1 Day5 2023.5.9 周二

English Learning - L3 作业打卡 Lesson1 Day5 2023.5.9 周二 引言&#x1f349;句1: Fast loud music is popular with many people .成分划分爆破语调 &#x1f349;句2: They may say the music is red hot, especially the kind called Dixieland jazz.成分划分爆破语调 &a…

AI孙燕姿项目实现

最近在b站刷到很多关于ai孙笑川唱的歌曲&#xff0c;加上最近大火的ai孙燕姿&#xff0c; 这下“冷门歌手”整成热门歌手了 于是写下一篇文章&#xff0c; 如何实现属于的ai歌手。 注意滥用ai&#xff0c;侵犯他人的名誉是要承担法律责任的 下面是一些所需的文件链接&#xff…

低代码信创开发核心技术(一):基于Vue.js的描述依赖渲染DDR实现模型驱动的组件

前言 随着数字化转型的不断发展&#xff0c;低代码开发平台已成为企业快速建立自己的应用程序的首选方案。然而&#xff0c;实现这样一个平台需要具备高效、灵活和可定制化的能力。这正是基于描述依赖渲染&#xff08;Description dependency rendering&#xff09;所实现的。…

C语言CRC-16 XMODEM格式校验函数

C语言CRC-16 XMODEM格式校验函数 CRC-16校验产生2个字节长度的数据校验码&#xff0c;通过计算得到的校验码和获得的校验码比较&#xff0c;用于验证获得的数据的正确性。基本的CRC-16校验算法实现&#xff0c;参考&#xff1a; C语言标准CRC-16校验函数。 不同应用规范通过对…

三分钟阿里云服务器u1通用算力型性能、使用限制及费用说明

阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xff0c…

C/C++每日一练(20230512) 成绩打印、补齐数组、水仙花数

目录 1. 成绩打印 ※ 2. 按要求补齐数组 &#x1f31f;&#x1f31f;&#x1f31f; 3. 水仙花数 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 成绩打印 一个班有10个同学&am…

隐语v0.8.2版本更新,首次发布TEEU

隐语v0.8.2版本更新&#x1f31f; 应用层 机器学习&#xff1a; - MPC 纵向 LR &#xff08;SSRegression&#xff09;新增 Policy SGD 优化器和 Early Stopping 支持&#xff0c;减少调参成本&#xff0c;加快收敛速度&#xff1b; - WOE 分箱进行了若干优化&#xff0c;性…

本地搭建wamp服务器并内网穿透实现无公网IP远程访问

文章目录 前言1.Wamp服务器搭建1.1 Wamp下载和安装1.2 Wamp网页测试 2. Cpolar内网穿透的安装和注册2.1 本地网页发布2.2 Cpolar云端设置2.3 Cpolar本地设置 3. 公网访问测试4. 结语 转载自cpolar极点云的文章&#xff1a;无公网IP&#xff1f;教你在外远程访问本地Wamp服务器「…

前端CSS学习(三)

1、盒子模型 盒子的概念1、页面中的每一个标签&#xff0c;都可看做是一 个“盒子” &#xff0c;通过盒子的视角更方便的进行布局2、浏览器在渲染 (显示)网页时&#xff0c;会将网页中的元素看做是一个个的矩形区域&#xff0c;我们也形象的称之为盒子CSS中规定每个盒子分别由…

Postman安装及入门接口测试使用步骤

前言 在软件测试行业中&#xff0c;作为一款比jemter更便捷更好用的软件测试工具&#xff0c;postman以其便捷灵活性首当其冲&#xff0c;成为当今测试行业领域使用较广泛的主流系统软件接口测试工具。今天Darren洋为大家讲解postman这款软件测试工具的下载安装及入门接口测试步…

Linux权限 - 概念与管理 | 文件权限的修改与转让 【详解】

目录 Linux权限 Linux权限的概念 Linux权限的基础操作 (1).实现用户账号的切换 (2).仅提升当前指令的权限 Linux权限管理 1、文件访问者的分类&#xff08;人&#xff09; 2、文件类型和访问权限&#xff08;事物属性&#xff09; 3、文件权限值的表示方法 4、文件访…

刷题刷题。

自然数拆分 利用step记录组合情况&#xff0c;只用sum不能判断组合情况 1.选择dfs原因&#xff1a;产生排列组合&#xff0c;和为7&#xff0c;step为8&#xff0c;其中7个空位&#xff0c;第8个step为输出&#xff1b; 参量的设置sum&#xff0c;step (进入下一层&#xff09;…

ThingsBoard教程(四十):规则节点解析 计算增量节点 Calculate delta

本篇文章介绍一个ThingsBoard 规则引擎中的一个节点,Calculate delta Calculate delta 计算增量 该节点可以在规则中获取上一次遥测的值,以此可以实现二次遥测的差。比如一个设备,一天上传一次数据,如果你要对比今天和昨天的数据,并将两者数据差保存到数据库,就能够使用…

Spring MVC

目录 什么是Spring MVC MVC定义 MVC和Spring MVC的关系 怎么学Spring MVC 创建Spring MVC项目 0.使用Spring Boot来创建Spring MVC项目 1.实现连接 2.获取参数 获取单个参数 获取多个参数 获取对象 后端参数重命名 获取JSON对象 从基础的URL中获取参数 上传文件Re…

1688获取商品api接口

作为一名技术爱好者&#xff0c;我们总会遇到各种各样的技术问题&#xff0c;需要寻找合适的技术解决方案。而在互联网时代&#xff0c;我们可以快速通过搜索引擎获取丰富的技术资源和解决方案。然而&#xff0c;在不同的技术分享中&#xff0c;我们常常会遇到质量参差不齐的文…

linux中查看某个文件夹下文件的个数和大小

一、统计某个目录的文件和子目录的大小 1、stat指令 stat命令 主要用于显示文件或文件系统的详细信息&#xff0c;该命令的语法格式如下&#xff1a; -f  不显示文件本身的信息&#xff0c;显示文件所在文件系统的信息-L  显示符号链接-t  简洁模式&#xff0c;只显示…

如何压缩pdf文件大小?四种方法随意选择

如何压缩pdf文件大小&#xff1f;PDF文件格式由于其跨平台性&#xff0c;易于浏览、打印和传输等特点&#xff0c;在现代社会中广泛应用于各个领域。然而&#xff0c;随着PDF文件越来越大&#xff0c;传输及存储所需的时间也会变得越来越长&#xff0c;从而降低了工作效率。在这…

如何用ChatGPT协助搭建品牌视觉体系(VI)?

该场景对应的关键词库&#xff08;18个&#xff09;&#xff1a; VI体系、品牌、目标市场、品牌DNA、人群特征、设计理念、标志设计、配色方案、字体选择、图形元素、价值观、形象、客户经理、需求、品牌定位、目标受众、主色调、辅助色 提问模板&#xff08;2个&#xff09;&…