大聪明教你学Java设计模式 | 第十三篇:观察者模式

article/2025/9/10 16:23:53

前言

🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~

大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案。大聪明本着“独乐乐不如众乐乐”的宗旨与大家分享一下设计模式的学习心得。

观察者模式

🍓🍓什么是观察者模式🍓🍓

在讲解观察者模式之前,我们先来看一下它的定义👇

观察者模式(又被称为模型(Model)- 视图(View)模式、源 - 收听者(Listener)模式、从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。即当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。它通常被用来实现事件处理系统。

在现实生活种很多对象都不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨,会导致部分商家高兴,导致消费者伤心;当我们开车到十字路口时,看到红灯会停车,看见绿灯才会继续开车向前走;在学校里,老师听见上课铃就会去教室给学生上课,学生们也会在教室里坐好,下课铃打响后,老师和学生才会走出教室。在软件世界也是这样,比如 Excel 中的数据与折线图(或饼状图、柱状图),当数据发生改变时,对应的图也会发生改变。这些例子都是观察者模式的良好体现。通过这些例子我们也能看出观察者模式的应用场景:当我们遇到一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都需要得到通知并同时改变状态的场景时,就可以选择使用观察者模式来实现

观察者模式共分为四个角色:

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

🍓🍓观察者模式的实现🍓🍓

上面我们提到了【铃声 - 老师/学生】的例子,那么我们就用观察者模式实现一下这个例子,在写代码之前我们先简单分析一下,看看应该如何实现👇

🍋第一步: 我们需要定义一个铃声事件(RingEvent)类,它记录了铃声的类型(上课铃声/下课铃声)。

🍋第二步: 定义一个学校的铃(BellEventSource)类,它是事件源,是观察者目标类,该类里面包含了监听器容器 listener,可以绑定监听者(学生或老师),并且有产生铃声事件和通知所有监听者的方法。

🍋第三步: 定义铃声事件监听者(BellEventListener)类,它是抽象观察者,其中包含了铃声事件处理方法 。

🍋第四步: 定义老师类(TeachEventListener)和学生类(StuEventListener),它们是事件监听器,也是具体观察者,听到铃声会去上课或下课。

接下来我们看看具体的代码👇

import java.util.*;/*** 观察者模式实现【铃声 - 老师/学生】的例子* @description: BellEventTest* @author: 庄霸.liziye* @create: 2022-04-12 16:58**/
public class BellEventTest {public static void main(String[] args) {/*** 铃(事件源)*/BellEventSource bell = new BellEventSource();//注册监听器(老师)bell.addPersonListener(new TeachEventListener());//注册监听器(学生)bell.addPersonListener(new StuEventListener());//打上课铃声bell.ring(true);System.out.println("-------我是分割线-------");//打下课铃声bell.ring(false);}
}
/*** 铃声事件类:用于封装事件源及一些与事件相关的参数*/
class RingEvent extends EventObject {private static final long serialVersionUID = 1L;/*** true表示上课铃声,false表示下课铃声*/private boolean sound;public RingEvent(Object source, boolean sound) {super(source);this.sound = sound;}public void setSound(boolean sound) {this.sound = sound;}public boolean getSound() {return this.sound;}
}
/*** 目标类:事件源,铃*/
class BellEventSource {/*** 监听器容器*/private List<BellEventListener> listener;public BellEventSource() {listener = new ArrayList<BellEventListener>();}/*** 给事件源绑定监听器*/public void addPersonListener(BellEventListener ren) {listener.add(ren);}/*** 事件触发器:敲钟,当铃声sound的值发生变化时,触发事件。*/public void ring(boolean sound) {String type = sound ? "上课铃" : "下课铃";System.out.println(type + "响啦!");RingEvent event = new RingEvent(this, sound);//通知注册在该事件源上的所有监听器调notifies(event);}/*** 当事件发生时,通知绑定在该事件源上的所有监听器做出反应(用事件处理方法)*/protected void notifies(RingEvent e) {BellEventListener ren = null;Iterator<BellEventListener> iterator = listener.iterator();while (iterator.hasNext()) {ren = iterator.next();ren.heardBell(e);}}
}/*** 抽象观察者类:铃声事件监听器*/
interface BellEventListener extends EventListener {/*** 事件处理方法,听到铃声*/public void heardBell(RingEvent e);
}/*** 具体观察者类:老师事件监听器*/
class TeachEventListener implements BellEventListener {@Overridepublic void heardBell(RingEvent e) {if (e.getSound()) {System.out.println("老师:上课...");} else {System.out.println("老师:下课...");}}
}/*** 具体观察者类:学生事件监听器*/
class StuEventListener implements BellEventListener {@Overridepublic void heardBell(RingEvent e) {if (e.getSound()) {System.out.println("同学们,都在教室里做好了...");} else {System.out.println("同学们,都出去玩了...");}}
}

🍎🍎执行结果🍎🍎

在这里插入图片描述

🍓🍓观察者模式的优、缺点🍓🍓

最后我们总结一下观察者模式的优点与缺点👇

🍌🍌优点🍌🍌

降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系(符合依赖倒置原则);目标与观察者之间建立了一套触发机制。

🍌🍌缺点🍌🍌

🍋缺点一: 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

🍋缺点二: 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

🍋缺点三: 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西


http://chatgpt.dhexx.cn/article/gRaIhQUa.shtml

相关文章

Java 设计模式(十三):代理模式

参考链接&#xff1a;代理模式-Proxy Pattern 近年来&#xff0c;代购已逐步成为电子商务的一个重要分支。代购简单来说就是找人帮忙购买所需要的商品&#xff0c;代购网站就是其中一种产物&#xff0c;它为消费者提供在线的代购服务&#xff0c;如果看中某国外购物网站上的商…

Java设计模式:观察者模式

一、什么是观察者模式&#xff1f; 又叫做发布-订阅模式&#xff0c;定义对象间一种一对多的依赖关系&#xff0c;使得每当一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下&#xff1a; 其中涉及到四种角色&#xff1a; 1.抽象目标&a…

初探Java设计模式------观察者模式

前言 最近刚开始学习RxJava&#xff0c;众所周知&#xff0c;Rxjava就是扩展的观察者模式&#xff0c;所以想学习Rxjava&#xff0c;先入手了解一下观察者模式是很有必要的。那么今天就先稍微了解一下什么是观察者模式。 定义 观察者(Observer)模式&#xff1a;是对象的行为模…

java设计模式(3)--观察者模式

&#xff08;一&#xff09;观察者模式 观察者模式定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 该模式有以下角色&#xff1a; &#xff08;1&#xff09;抽象主题&#xff08;Subjec…

Java设计模式之行为型:观察者模式

一、什么是观察者模式&#xff1a; 观察者模式又称为 发布-订阅模式&#xff0c;定义了对象之间一对多依赖关系&#xff0c;当目标对象(被观察者)的状态发生改变时&#xff0c;它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者&#xff0c;而这些观察者之间…

java监听设计模式(java观察者设计模式)

今天给大家分享一下观察者设计模式&#xff08;监听设计模式&#xff09;&#xff0c;该模式在很多主流得框架、源码中使用率非常高。在分享之前先给大家讲一个我们使用手机的一个场景&#xff0c;我们都用过手机&#xff0c;当我们手机来电话的时候&#xff0c;会有各种复杂的…

【十一】设计模式~~~结构型模式~~~代理模式(Java)

【学习难度&#xff1a;★★★☆☆&#xff0c;使用频率&#xff1a;★★★★☆】 6.1. 模式动机 在某些情况下&#xff0c;一个客户不想或者不能直接引用一个对 象&#xff0c;此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起…

JAVA架构之路(设计模式之观察者模式)

设计模式之观察者模式 定义&#xff1a;对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新 核心原理&#xff1a; 1.被观察者中维护一个观察者对象列表 2.观察者可新增可可移除。 需要角色&#…

程序员百宝箱---搭建自己专属的在线工具集

相信各位开发者/程序员在开发过程中都使用过各种在线工具吧。比如 1 校验或格式化 json 字符串 2 使用 base64 的加密解密&#xff0c;url 的编码解码 urlencode、urldecode 等 3 调试正则表达式是否正确 4 时间戳与格式化日期互相转换 5 文本对比&#xff0c;比较两个代码文件…

如何部署JSP应用到阿里云服务器上(一)

今天讲解一下如何部署JSP应用到阿里云服务器上&#xff0c;我使用的后台服务器是 Tomcat服务器&#xff0c;服务器应用的开发语言是 Java Web &#xff0c;后台的数据库使用的是MySQL 1 打开阿里云官方网站 https://www.aliyun.com/?utm_mediumtext&utm_sourcebdbrand&am…

开源项目精选推荐-杨小杰工具箱(YoungxjTools)

演示 预览地址: https://tools.yum6.cn/ 程序介绍 01、首页底部友情链接 02、网站公告页面功能 03、内置留言管理功能 04、后台网站信息设置 05、内置smtp发信配置 06、支持两主题的切换 07、关于页面支持留言, 08、程序已集成32种小工具 更多功能 介绍 请搭建 自行测试

C++构造函数之初始化列表

C构造函数之初始化列表 构造函数可以说是对于C每个类来说最重要的组成部分&#xff0c;之前也详细介绍过构造函数的相关知识&#xff0c;今天给构造函数进行补充&#xff0c;主要说明一下构造函数的初始值列表 一、初始化列表的写法 仍然以之前介绍构造函数时使用的学生类来…

C++初始化列表详解

目录&#xff1a; 定义 使用初始化列表的原因 必须使用初始化列表的时候 成员变量的顺序 定义 与其他函数不同&#xff0c;构造函数除了有名字&#xff0c;参数列表和函数体之外&#xff0c;还可以有初始化列表&#xff0c;初始化列表以冒号…

C++类的初始化列表

意义 初始化列表是类中构造函数的一部分&#xff0c;用于实例化类中变量时 赋初值。 需要先了解 构造函数的基本知识。CSDN-构造函数https://blog.csdn.net/weixin_44212838/article/details/124901019?spm1001.2014.3001.5501 用法 在函数头与函数体之间&#xff0c;用一…

详解初始化列表

初始化列表 定义 构造函数有个特殊的初始化方式叫“初始化表达式表”&#xff08;简称初始化列表&#xff09;。初始化列表位于函数参数表之后&#xff0c;却在函数体 {} 之前。这说明该表里的初始化工作发生在函数体内的任何代码被执行之前。 Date(int year, int month, in…

C++ 成员初始化列表

这里写目录标题 数据成员是某一个类的对象&#xff0c;且这个类没有默认构造函数数据成员的类型是const或者引用子类初始化父类初始化列表先后顺序使用成员初始化列表的优点 在以下三种情况下需要使用初始化成员列表&#xff1a; 需要初始化的数据成员是某一个类的对象&#xf…

【深入理解】初始化列表

目录 一、什么是初始化列表&#xff1f; 二、初始化列表长啥样&#xff1f; 三、初始化列表的特性 1、由于是在定义阶段进行的初始化&#xff0c;所以&#xff0c;只能在定义阶段进行的初始化类型便只能在初始化列表中进行初始化。 2、初始化顺序不是跟着初始化列表走的&#x…

C++ 初始化列表详解

目录 1.什么是初始化列表 2.什么时候需要使用初始化列表&#xff1f; 3.初始化列表的效率 4.初始化列表的初始化顺序 1.什么是初始化列表 class A { public:A(int value):m_dada(value){}private:int m_dada; }; 如上图&#xff0c;红色圈起来的部分&#xff0c;就是构造函…

【C++】-- 初始化列表

目录 一、用初始化列表初始化对象 1.初始化列表用法 2.初始化列表特性 二、explicit关键字 1.内置类型的隐式转换 2.如何避免单参构造函数初始化发生隐式类型转换 三、匿名对象 1.匿名对象定义 2.匿名对象应用场景 创建一个类对象时&#xff0c;编译器通过调用构造函…

字符串逆序(数组倒序输出)

题目信息&#xff1a; 样例输出 copy edcba 新手导读&#xff1a; 题目描述信息&#xff1a;字符串的倒序输出肯定是字符数组的倒序输出&#xff1b; 输入信息&#xff1a;输入条件是最后字符不为回车&#xff1b;所以应该先用变量存入字符进行判断&#xff0c;再使用这个…