观察者模式全攻略:从设计原理到 SpringBoot 实践案例

news2024/9/20 11:15:00

🎯 设计模式专栏,持续更新中~~
欢迎订阅:JAVA实现设计模式 🛠️
希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动更新。

核心思想

观察者模式将**观察者(Observer)被观察者(Subject)**解耦,允许对象独立变化,并且当被观察者状态变化时,自动通知所有观察者。

观察者模式中的角色:

  1. Subject(被观察者):负责维护观察者列表,并在自身状态发生变化时通知所有观察者。它提供注册、移除和通知观察者的方法。
  2. Observer(观察者):定义一个接口,用于接收来自被观察者的通知。
  3. ConcreteSubject(具体被观察者):实现 Subject 接口,包含具体的状态,并在状态变化时调用 notifyObservers()
  4. ConcreteObserver(具体观察者):实现 Observer 接口,并在被观察者状态变化时更新自身。

观察者模式的 UML 原理类图

在这里插入图片描述

类图解释:

  1. Subject(被观察者接口)
    • 定义了方法 registerObserver()removeObserver()notifyObservers(),用于管理观察者的注册、移除和通知操作。
  2. Observer(观察者接口)
    • 定义了 update() 方法,观察者通过该方法接收来自被观察者的更新通知。
  3. ConcreteSubject(具体被观察者)
    • 维护一个观察者列表,并保存自身的状态。当状态发生变化时,调用 notifyObservers() 方法通知所有观察者。
    • 提供了 getState()setState() 方法来管理其状态,并在状态更新时调用通知方法。
  4. ConcreteObserver(具体观察者)
    • 持有对具体被观察者的引用,并实现 update() 方法,在被观察者状态变化时更新自身。

案例:天气预报系统 ☀️🌧️

场景说明

假设我们有一个天气预报系统,它实时监控天气数据,并向多个显示设备(如手机、电视、平板等)推送更新信息。系统中,气象站负责监控天气数据,当气温、湿度等信息发生变化时,它会通知所有的显示设备。这些设备会自动更新并显示最新的天气信息。

代码实现

Step 1: 定义 Observer 接口

// 观察者接口
public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

Step 2: 定义 Subject 接口

// 被观察者接口
public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

Step 3: 实现具体的 Subject

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

// 具体的被观察者:天气数据
public class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        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(temperature, humidity, pressure);  // 通知所有观察者
        }
    }

    // 当天气数据变化时,通知观察者
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();  // 通知观察者
    }
}

Step 4: 实现具体的 Observer

PhoneDisplay 观察者

// 具体观察者:手机显示器
public class PhoneDisplay implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    public void display() {
        System.out.println("Phone Display - Temp: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + "Pa");
    }
}

TVDisplay 观察者

// 具体观察者:电视显示器
public class TVDisplay implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    public void display() {
        System.out.println("TV Display - Temp: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + "Pa");
    }
}

Step 5: 测试观察者模式

public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建被观察者(天气数据)
        WeatherData weatherData = new WeatherData();

        // 创建观察者
        PhoneDisplay phoneDisplay = new PhoneDisplay();
        TVDisplay tvDisplay = new TVDisplay();

        // 注册观察者
        weatherData.registerObserver(phoneDisplay);
        weatherData.registerObserver(tvDisplay);

        // 模拟天气数据变化
        weatherData.setMeasurements(25.0f, 65.0f, 1013.0f);
        weatherData.setMeasurements(28.0f, 70.0f, 1010.0f);
    }
}

输出结果

Phone Display - Temp: 25.0°C, Humidity: 65.0%, Pressure: 1013.0Pa
TV Display - Temp: 25.0°C, Humidity: 65.0%, Pressure: 1013.0Pa
Phone Display - Temp: 28.0°C, Humidity: 70.0%, Pressure: 1010.0Pa
TV Display - Temp: 28.0°C, Humidity: 70.0%, Pressure: 1010.0Pa

Spring Boot 中使用观察者模式

Spring Boot 框架中,观察者模式主要可以通过 Spring 的事件驱动机制(Event Driven) 来实现。这是 Spring 框架对观察者模式的原生支持,允许不同的组件之间进行解耦通信,使得事件的发布者和监听者(观察者)可以独立变化。

Spring 事件驱动机制的核心概念:

  1. 事件(Event):类似于观察者模式中的 “Subject”(被观察者),当事件发生时,发布该事件。
  2. 事件监听器(Listener):类似于观察者,监听特定的事件并在事件发布时自动响应。
  3. 事件发布器(Publisher):发布事件,让所有监听器接收事件通知。

如何在 Spring Boot 中实现观察者模式:步骤详解**

Step 1: 创建自定义事件类

自定义事件类需要继承 Spring 提供的 ApplicationEvent 类。事件类可以封装一些需要传递的状态信息。

import org.springframework.context.ApplicationEvent;

// 自定义事件类,继承 ApplicationEvent
public class CustomEvent extends ApplicationEvent {
    private String message;

    public CustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

Step 2: 创建事件监听器

实现 ApplicationListener 接口,或使用注解 @EventListener 来创建监听器。监听器会在事件发布时自动被调用。

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

// 事件监听器
@Component
public class CustomEventListener {

    // 使用 @EventListener 注解监听自定义事件
    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("Received event - " + event.getMessage());
    }
}

Step 3: 创建事件发布器

使用 ApplicationEventPublisher 来发布事件。当需要通知观察者时,可以发布自定义事件。

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

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

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

    // 发布事件
    public void publishEvent(String message) {
        CustomEvent event = new CustomEvent(this, message);
        publisher.publishEvent(event);  // 发布自定义事件
    }
}

Step 4: 使用事件发布器

在 Spring Boot 应用中,通过事件发布器发布事件,触发所有监听该事件的观察者。

@RestController
public class BasicController {

    @PostMapping("/publish")
    void publish(@RequestBody String message){
        eventPublisher.publishEvent(message);
    }

}

输出结果

Received event - {
  "message": "hello"
}

Spring 事件驱动机制 VS 传统观察者模式

相似性:

  • 解耦:事件发布者(ApplicationEventPublisher)和事件监听器(ApplicationListener@EventListener)之间是解耦的,它们通过事件进行通信。
  • 一对多:一个事件可以被多个监听器同时监听,符合观察者模式的一对多依赖关系。

不同点:

  • 事件发布是异步的:Spring 的事件驱动机制允许事件的异步处理(如果配置了 @Async 注解),而传统的观察者模式通常是同步执行的。
  • 更加灵活的监听机制:Spring 提供了基于注解的 @EventListener,简化了监听器的注册过程,并可以使用条件过滤等高级功能。

Spring Boot 中,通过其内置的 事件驱动机制,可以轻松实现观察者模式。这种机制不仅保留了观察者模式的基本思想,还进一步增强了可扩展性和灵活性,支持异步处理、注解配置等高级特性。它在处理系统之间的解耦和事件驱动编程时尤其强大,非常适合使用在实际项目中。

使用观察者模式的典型场景:

  • 异步事件通知:如用户注册、邮件发送等需要异步处理的操作。
  • 解耦组件间的通信:模块化应用中,各模块之间的消息传递和事件处理。

总结

观察者模式 提供了一种解耦对象之间的依赖关系的方式,使得被观察者和观察者可以在不直接相互依赖的情况下进行通信。通过观察者模式,当一个对象发生变化时,所有依赖于它的对象都会自动收到通知并作出相应的处理。

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

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

相关文章

web学习——VUE

VUE&Element 今日目标&#xff1a; 能够使用VUE中常用指令和插值表达式能够使用VUE生命周期函数 mounted能够进行简单的 Element 页面修改能够完成查询所有功能能够完成添加功能 1&#xff0c;VUE 1.1 概述 接下来我们学习一款前端的框架&#xff0c;就是 VUE。 Vue 是…

JAVA并发编程系列(8)CountDownLatch核心原理

拼多多 D2面试&#xff0c;现场编程模拟拼团&#xff0c;10人拼团成功。限时2分钟&#xff01;开始吧.....&#xff01;在面试过程经常有算法题、模拟现实案例、经典功能设计、核心原理分析这种。这些看似简单&#xff0c;实际需要候选人有非常扎实的基础&#xff0c;才能应付这…

maxwell 输出消息到 kafka

文章目录 1、kafka-producer2、运行一个Docker容器&#xff0c;该容器内运行的是Zendesk的Maxwell工具&#xff0c;一个用于实时捕获MySQL数据库变更并将其发布到Kafka或其他消息系统的应用3、进入kafka容器内部4、tingshu_album 数据库中 新增数据5、tingshu_album 数据库中 更…

【Django5】django的helloworld

安装django pip install djangoDjango官方中文文档 https://docs.djangoproject.com/zh-hans/5.1/Github链接 https://github.com/django/django创建Django项目 cd到想要创建项目的文件夹下&#xff0c;输入以下命令创建项目 这行代码将会在当前目录下创建一个 mysite 目录 …

Seeing What You Said Talking Face Generation Guided论文随记

Seeing What You Said Talking Face Generation Guided by a Lip Reading Expert 文章认为以往工作很少关注唇语清晰度&#xff0c;希望通过惩罚不准确结果来提升唇部区域动作的可理解性。 原文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Wang_…

【吊打面试官系列-MySQL面试题】LIKE 声明中的%和_是什么意思?

大家好&#xff0c;我是锋哥。今天分享关于【LIKE 声明中的&#xff05;和_是什么意思&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; LIKE 声明中的&#xff05;和_是什么意思&#xff1f; &#xff05;对应于 0 个或更多字符&#xff0c;_只是 LIKE 语句中的…

PSINS工具箱函数介绍——myfig

文章目录 关于工具箱使用方法与主要作用例程实践运行代码函数解析关于工具箱 myfig是关于绘图的函数,位置在: p s i n s / b a s e / p l o t psins/\ base/\ plot

深入理解 Linux 内核网络协议栈

Linux 作为全球广泛应用的操作系统&#xff0c;凭借其稳定、高效和开源的特点&#xff0c;已成为服务器、嵌入式系统和个人电脑等领域的首选系统。而在 Linux 系统的核心中&#xff0c;网络协议栈承担了网络数据通信的关键任务。理解 Linux 内核网络协议栈的工作原理&#xff0…

加密与安全_三种常见的注入攻击

文章目录 概述注入攻击类型及危害注入攻击的工作原理SQL注入代码注入XSS&#xff08;跨站脚本&#xff09; SQL注入SQ L注入攻击的本质常见误区及注入点误区1&#xff1a;SQL注入仅发生在GET请求中误区2&#xff1a;没有返回数据的接口不易受注入影响误区3&#xff1a;SQL注入的…

SpringSecurity原理解析(八):CSRF防御解析

一、CsrfFilter CsrfFilter 主要功能是用来防止csrf攻击 一、什么是CSRF攻击 跨站请求伪造&#xff08;英语&#xff1a;Cross-site request forgery&#xff09;&#xff0c;也被称为 one-click attack 或者 session riding&#xff0c;通常缩写为 CSRF 或者 XSRF&#xff0c…

关于广告投放平台的设计

文章目录 广告投放平台的作用广告平台的核心功能 最近在看关于广告投放平台相关的设计&#xff0c;倒是没看到完整的案例介绍。整理一下收集的各种信息&#xff0c;假如需要设计一个广告投放系统&#xff0c;该怎么做&#xff1f; 广告投放平台的作用 要体现广告投放平台的作用…

如何实现LLM的通用function-calling能力?

众所周知&#xff0c;LLM的函数function-calling能力很强悍&#xff0c;解决了大模型与实际业务系统的交互问题。其本质就是函数调用。 从openai官网摘图&#xff1a; 简而言之&#xff1a; LLM起到决策的作用&#xff0c;告知业务系统应该调用什么函数&#xff0c;以及入参是…

动物识别系统Python+卷积神经网络算法+TensorFlow+人工智能+图像识别+计算机毕业设计项目

一、介绍 动物识别系统。本项目以Python作为主要编程语言&#xff0c;并基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;通过收集4种常见的动物图像数据集&#xff08;猫、狗、鸡、马&#xff09;然后进行模型训练&#xff0c;得到一个识别精度较高的模型文件&am…

Android ImageView支持每个角的不同半径

Android ImageView支持每个角的不同半径 import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import an…

css 控制虚线刻度尺寸

文章目录 css效果 css <div style"width: 100%; height: 1px;background-image: linear-gradient(to right, #545454 0%, #545454 80%, transparent 5%);background-size: 15px 10px;background-repeat: repeat-x; margin: 0 auto;"></div>效果

W外链如何实现长链接转短链接教程

要实现微信外链的长链接转短连接&#xff0c;可以借助专门的工具来简化流程并增加链接的安全性和稳定性。 以下是一个具体方案&#xff1a; 使用W外链工具 W外链是一款集成了多种功能的微信外链生成器&#xff0c;包括但不限于短链制作、活码生成、微信外链制作等。以下是使用…

设置PDF打开密码

为PDF文件设置打开密码是一种有效的保护措施&#xff0c;它能防止未经授权的用户访问文件内容。以下是一份专业指南&#xff0c;详细介绍如何为PDF文件设置打开密码。 打开pdf编辑器&#xff0c;我们点击工具栏中的【文件】功能&#xff0c;选择里面的【属性】 然后在属性设置…

英飞凌PSoC4000T的GPIO中断示例工程

关于PSoC4000T的初步介绍见:英飞凌MCU第五代高性能CAPSENSE技术PSoC4000T_psoc 4000t-CSDN博客 下面这个工程,在modustoolbox中可编译、下载到开发板、debug调试。 编译时会用到mtb_shared这个库: 已经pdl这个periperal driver library库:

SMS over IP原理

目录 1. 短消息业务的实现方式 2. 传统 CS 短消息业务中的发送与送达报告 3. MAP/CAP 信令常见消息 4. SMS over IP 特点概述 5. SMS over IP 中的主要流程 5.1 短消息注册流程(NR 或 LTE 接入) 5.2 短消息发送(MO)流程(NR 或 LTE 接入) 5.3 短消息接收(MT)流程(NR 或…

国际知名度最高的华人改名大师颜廷利:当代最牛的易经姓名学泰斗

国际知名度最高的华人改名大师颜廷利&#xff1a;当代最牛的易经姓名学泰斗 颜廷利教授&#xff0c;一位在姓名学领域享有盛誉的专家&#xff0c;其声誉根植于齐鲁大地&#xff0c;山东济南历城区唐王镇&#xff08;现升级为历城区唐王街道办事处&#xff09;。他的工作基地不仅…