Java设计模式之行为型-观察者模式(UML类图+案例分析)

news2024/11/8 18:30:59

目录

一、基本概念

二、UML类图

三、角色设计

四、代码实现

案例一

案例二

案例三

五、总结


一、基本概念

观察者先订阅被观察者对象,当被观察者的状态发生变化时,观察者可以及时收到消息,在这种模式当中,被观察者维护了一个观察者列表,并提供了添加、删除、通知观察者的方法。

二、UML类图

三、角色设计

角色描述
Blogger被观察者接口(博主),内部提供了注册、删除、通知观察者的方法
CSDNBlogger具体被观察者对象接口的实现类(CSDN),内部维护一个观察者列表
Fans观察者接口(粉丝),在被观察者通知时更新自己
FansObserver具体的观察者,当被通知时,可根据实际需求决定如何更新自己的状态

四、代码实现

这边我分享了三个案例,分别是通过自定义撰写、JDK源码封装和Spring框架封装三种去实现。

案例一

假设有一个被观察者:CSDN博主,它有2个粉丝分别是Jack和Tom,当CSDN发布了通知,对应的粉丝都会收到私信。

1、定义被观察者接口。

/**
 * 博主
 * @author HTT
 */
public interface Blogger {

    /**
     * 新增粉丝
     */
    void addFans(Fans fans);

    /**
     * 移除粉丝
     */
    void removeFans(Fans fans);

    /**
     * 通知粉丝
     */
    void sendMessage(String message);
}

2、被观察者具体实现类。

CSDN博主:

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

public class CSDN implements Blogger {

    private List<Fans> fansList = new ArrayList<>();


    @Override
    public void addFans(Fans fans) {
        this.fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        this.fansList.remove(fans);
    }

    @Override
    public void sendMessage(String message) {
        for(Fans fans : fansList){
            fans.receiveMessage(message);
        }
    }
}

3、定义观察者接口。

/**
 * 粉丝
 * @author HTT
 */
public interface Fans {

    void receiveMessage(String message);

}

4、定义具体观察者实现类。

Jack粉丝:

public class JackFans implements Fans {
    @Override
    public void receiveMessage(String message) {
        System.out.println("Jack收到了私信:"+message);
    }
}

Tom粉丝:

public class TomFans implements Fans {

    @Override
    public void receiveMessage(String message) {
        System.out.println("Tom收到了私信:"+message);
    }
}

5、测试运行:

public class Main {

    public static void main(String[] args) {
        Blogger blogger = new CSDN();
        Fans jackFans = new JackFans();
        Fans tomFans = new TomFans();
        blogger.addFans(jackFans);
        blogger.addFans(tomFans);
        blogger.sendMessage("CSDN发布了《关于社区整顿的通知》");
        blogger.removeFans(jackFans);
        blogger.sendMessage("CSDN发布了《关于博客发布调整的通知》");

    }
}

6、运行结果:

案例二

这个案例依旧是实现上述逻辑,只不过我们使用JDK提供的接口去实现,过程如下。

1、定义被观察者具体实现类。

CSDN博主:

import java.util.Observable;

public class CSDN extends Observable {

    @Override
    public void notifyObservers(Object arg) {
        //修改状态为可以群发
        setChanged();
        //调用父类的notifyObservers 群发消息
        super.notifyObservers(arg);
    }
}

2、定义观察者具体实现类。

Tom粉丝:

import java.util.Observable;
import java.util.Observer;

public class TomFans implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Tom收到了私信:"+arg);
    }
}

Jack粉丝:

import java.util.Observable;
import java.util.Observer;

public class JackFans implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Jack收到了私信:"+arg);
    }
}

3、测试运行:

import java.util.Observable;
import java.util.Observer;

public class Main {

    public static void main(String[] args) {
        Observable csdn = new CSDN();
        Observer tomFans = new TomFans();
        Observer jackFans = new JackFans();
        csdn.addObserver(tomFans);
        csdn.addObserver(jackFans);
        csdn.notifyObservers("《关于整顿社区的通知》");
        csdn.deleteObserver(tomFans);
        csdn.notifyObservers("《关于更新CSDN社区的通知》");
    }
}

4、运行结果:

有了JDK提供的接口去实现,整体代码结构更简洁和方便了! 

案例三

这个案例依旧是上述逻辑,只不过我们这边使用Spring提供的封装事件的监听去实现,这边省去了搭建Spring框架的流程,直接看核心代码就行!

ApplicationEvent和Listener实际上就是Spring基于观察者模式设计的发布-订阅事件模型。

1、ApplicationEvent :自定义的事件对象,用于表示具体的事件。

CSDN博主:

import org.springframework.context.ApplicationEvent;

public class CSDN extends ApplicationEvent {

    private String message;

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

    public String getMessage(){
        return this.message;
    }

    public void setMessage(String message){
        this.message = message;
    }

}

2、ApplicationListener:事件监听器接口,用于监听特定事件。 

Jack粉丝:

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

@Component
public class JackFans implements ApplicationListener<CSDN> {
    @Override
    public void onApplicationEvent(CSDN event) {
        System.out.println("Jack收到私信:"+event.getMessage());
    }
}

Tom粉丝: 

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

@Component
public class TomFans implements ApplicationListener<CSDN> {
    @Override
    public void onApplicationEvent(CSDN event) {
        System.out.println("Tom收到私信:"+event.getMessage());
    }
}

3、单元测试

ApplicationContext:Spring上下文,用于广播ApplicationEvent,并通知相关的ApplicationListener。

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

import javax.annotation.Resource;

@SpringBootTest
class ObserverApplicationTests {

    @Resource
    private ApplicationContext applicationContext;

    @Test
    void contextLoads() {
        CSDN csdn = new CSDN(this,"发布了《关于整顿社区的通知》");
        applicationContext.publishEvent(csdn);
    }

}

4、运行结果

通过这种方式,不同的业务逻辑模块就可以不依赖于具体的实现,只通过监听特定事件来响应,从而实现解耦。

事件驱动的编程方式还有以下优点:

1、降低耦合度

2、提高模块间的独立性和可重用性

3、易于扩展和维护

总的来说,ApplicationEvent和Listener的设计初衷就是为了解耦和提高系统的扩展性、稳定性。 

五、总结

这边我举了3个案例去更好的理解观察者模式,在我们日常开发的工作中要时刻提醒我们自己的编码思想,利用好这些优秀的前辈提供给我们的精髓,应用到实际业务场景中去,精益求精!

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

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

相关文章

回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测

回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测 目录 回归预测 | MATLAB实现SSA-DBN麻雀算法优化深度置信网络的数据多输入单输出回归预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 基于麻雀算法优化深度置信网络(SSA-DB…

【Java】顺序表ArrayList

文章目录 一、顺序表二、ArrayList 的简介三、ArrayList 的使用3.1 构造方法3.2 常见操作3.3 遍历方法3.4 扩容机制 四、ArrayList 的模拟实现五、ArrayList 的使用案例5.1 扑克牌案例5.2 杨辉三角案例 六、ArrayList 存在的问题 一、顺序表 顺序表&#xff08;Sequential Lis…

JS中的字典和散列表

前言 除了集合&#xff0c;我们还可以用字典和散列表来存储唯一值。 集合学习请见&#xff1a; 自定义集合和ES6集合http://t.csdn.cn/RcznA 在集合中&#xff0c;我们关注的是每个值本身。并将它作为主要元素。 而字典和散列表都是以[键:值]的形式来存储数据。 不同的…

Linux中vim的预备代码(prepare-code)设置

1、进入以下目录&#xff1a; /home/yys/.vim/plugged/prepare-code/snippet注意&#xff1a;yys是我个人的账号名称&#xff0c;每个人的都不一样&#xff01; 2、修改相应的预备代码&#xff0c;比如snippet.c 修改完之后保存&#xff0c;之后再创建c文件则会自动初始化有…

Python如何免费获取付费文档的数据, 保存word文档

目录标题 前言开发环境:模块使用:代码实现步骤:代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 开发环境: python 3.8 pycharm 模块使用: requests --> pip install requests re json time base64 docx --> pip install python-docx 第三方模…

【C++初阶】string类常见题目详解(二) —— 把字符串转换成整数、反转字符串、反转字符串 II、反转字符串中的单词 III、字符串相乘

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;C初阶】s…

ElasticSearch学习02——Kibana安装

ElasticSearch学习02——Windows下Kibana安装 Kibana是界面化的查询数据的工具&#xff0c;下载时尽量下载与ElasicSearch一致的版本。 1、下载对应版本的Kibana ​ 有了ElasticSearch安装的经验&#xff0c;我们发现了ES和JDK有着版本对应的关系&#xff0c;Kibana和ES共同为…

【Linux基础命令】nmtui命令使用实战

前言 linux常用命令专栏已进入尾声&#xff0c;大约90个命令是日常工作中常用的&#xff0c;在拓展一些不常用的&#xff0c;也就100左右。 是不是总结下来后&#xff0c;就感觉要学的内容没有那么多了。 当然有些专属的基础命令不在本专栏内&#xff0c;比如LVM管理命令&am…

微信读书:从Paxos到Zookeeper:分布式一致性原理与实践(阅读摘录)

微信读书&#xff1a;从Paxos到Zookeeper&#xff1a;分布式一致性原理与实践&#xff08;阅读摘录&#xff09; 阅读地址 CAP理论 CAP理论告诉我们&#xff0c;一个分布式系统不可能同时满足一致性(C&#xff1a;Consistency)、可用性(A&#xff1a;Availability)和分区容错…

Andriod 开发 SearchView默认弹出软键盘

SearchView默认弹出软键盘&#xff0c;遮挡了主界面 这很明显是SearchView是默认自动获取了焦点&#xff0c;所以上网搜了一下如何清除焦点&#xff1a; SearchView searchView getActivity().findViewById(R.id.searchViewSearchbar); searchView.clearFocus(); 然而没用&…

零拷贝原理

在实际应用中&#xff0c;如果我们需要把磁盘中的某个文件内容发送到远程服务器上&#xff0c;那么它必 须要经过几个拷贝的过程。从磁盘中读取目标文件内容拷贝到内核缓冲区&#xff0c;CPU 控制器再把内核缓冲区的数据赋值到用户空间的缓冲区中&#xff0c; 接着在应用程序中…

Arrays类概述,Lambda表达式

数组操作工具类&#xff0c;专门用于操作数组元素 2&#xff1a;常用API Lambda概述 Lambda表达式是JDK开始后的一种新语法形式作用&#xff1a;简化匿名内部类的代码写法 格式&#xff1a; 注意&#xff1a;Lambda表达式只能简化函数式接口的匿名内部类的写法形式。 什么是…

JAVA-编程基础-07-面向对象思想

Lison <dreamlison163.com>, v1.0.0, 2023.03.26 JAVA-编程基础-07-面向对象思想 文章目录 JAVA-编程基础-07-面向对象思想一、三大特性封装继承多态 二、类图泛化关系 (Generalization)实现关系 (Realization)聚合关系 (Aggregation)组合关系 (Composition)关联关系 (A…

使用vim编辑器,进行保存时报错:E382: Cannot write, ‘buftype‘ option is set

目录 一、背景 1.1使用vim 进行:wq保存时&#xff0c;报错&#xff1a;E382: Cannot write, buftype option is set 1.2 产生原因 二、解决 2.1 解决办法 2.2 还原 一、背景 1.1使用vim 进行:wq保存时&#xff0c;报错&#xff1a;E382: Cannot write, buftype option i…

【UnityDOTS 二】Entity的理解

Entity的理解 Entity作为一种对CPU的Cache友好的编码方式&#xff0c;是DOTS中重要的编码流程与思想。需要程序员由OOP的思想转为DOD的思想&#xff0c;即&#xff1a;面向数据的编码方式。 Unity的ECS&#xff1a; Entity&#xff1a;只是一个代表&#xff0c;用于快速查找数…

前端Vue基于腾讯地图Api实现的选择位置组件 返回地址名称详细地址经纬度信息

前端Vue基于腾讯地图Api实现的选择位置组件 返回地址名称详细地址经纬度信息&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13310 效果图如下&#xff1a; #### 使用方法 使用方法 <!-- leftTitle:左边标题 name&…

哈工大计算机网络课程网络层协议详解之:路由算法概述与链路状态路由算法

哈工大计算机网络课程网络层协议详解之&#xff1a;路由算法概述与链路状态路由算法 在前面的小节中&#xff0c;我们介绍了网络中路由器的路由与转发等功能。我们说作为网络层&#xff0c;从功能上来说&#xff0c;核心功能就是要实现路由和转发。 对于转发来说&#xff0c;实…

PyTorch开放神经网络交换(Open Neural Network Exchange)ONNX通用格式模型的熟悉

我们在深度学习中可以发现有很多不同格式的模型文件&#xff0c;比如不同的框架就有各自的文件格式&#xff1a;.model、.h5、.pb、.pkl、.pt、.pth等等&#xff0c;各自有标准就带来互通的不便&#xff0c;所以微软、Meta和亚马逊在内的合作伙伴社区一起搞一个ONNX(Open Neura…

Redis - 优惠券秒杀、库存超卖、分布式锁、Redisson

文章目录 一、优惠券秒杀1.1 全局唯一ID概述1.2 Redis实现全局唯一ID1.3 添加优惠券1.3.1 entity1.3.2 Controller1.3.3 Service层1.3.4 测试 1.4 优惠券秒杀下单1.4.1 entity1.4.2 Controller1.4.3 Service1.4.3 测试 1.5 库存超卖问题1.5.1 库存超卖原因1.5.2 介绍乐观锁1.5.…

【博学谷学习记录】超强总结,用心分享 | 架构师 Jenkins学习总结

文章目录 Jenkins介绍背景应用场景主从集群 Jenkins使用访问Jenkins获取管理员密码进入jenkins主页修改管理员密码 安装插件插件安装 全局工具配置 企业实战pipeline(重点)创建PipelinePipeline语法语法示例 Jenkins介绍 背景 ​ Jenkins&#xff0c;之前叫做Hudson&#xff…