Java设计模式之(十二)——观察者模式

article/2025/9/10 15:46:00

1、什么是观察者模式?

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

观察者模式(Observer Design Pattern):在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会得到通知并自动更新。

说人话:也叫发布订阅模式,能够很好的解耦一个对象改变,自动改变另一个对象这种情况。

2、观察者模式定义

image-20210919214718188

①、Subject 被观察者

定义被观察者必须实现的职责, 它必须能够动态地增加、 取消观察者。 它一般是抽象类或者是实现类, 仅仅完成作为被观察者必须实现的职责: 管理观察者并通知观察者。

②、Observer观察者

观察者接收到消息后, 即进行update(更新方法) 操作, 对接收到的信息进行处理。

③、ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑, 同时定义对哪些事件进行通知。

④、ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同, 各个观察者有自己的处理逻辑。

3、观察者模式通用代码

/*** 观察者*/
public interface Observer {// 更新方法void update();
}
/*** 具体观察者*/
public class ConcreteObserver implements Observer{@Overridepublic void update() {System.out.println("接受到信息,并进行处理");}
}
/*** 被观察者*/
public abstract class Subject {// 定义一个被观察者数组private List<Observer> obsList = new ArrayList<>();// 增加一个观察者public void addObserver(Observer observer){obsList.add(observer);}// 删除一个观察者public void delObserver(Observer observer){obsList.remove(observer);}// 通知所有观察者public void notifyObservers(){for (Observer observer : obsList){observer.update();}}
}
/*** 具体被观察者*/
public class ConcreteSubject extends Subject{// 具体的业务public void doSomething(){super.notifyObservers();}
}
public class ObserverClient {public static void main(String[] args) {// 创建一个被观察者ConcreteSubject subject = new ConcreteSubject();// 定义一个观察者Observer observer = new ConcreteObserver();// 观察者观察被观察者subject.addObserver(observer);subject.doSomething();}
}

4、JDK 实现

在 JDK 的 java.util 包下,已经为我们提供了观察者模式的抽象实现,感兴趣的可以看看,内部逻辑其实和我们上面介绍的差不多。

观察者 java.util.Observer

image-20210920164737899

被观察者 java.util.Observable

image-20210920165032781

image-20210920165205123

5、实例

用户进行注册,注册完成之后,会发一封欢迎邮件。

5.1 普通实现

image-20210920220940735
public class UserController {public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则发送一封欢迎邮件if(userId != null){Mail.sendEmail(userId);}}public Long saveUser(String userName, String passWord){return 1L;}
}

上面的注册接口实现了两件事,注册和发送邮件,很明显违反了单一职责原则,但假设这个注册需求是不是经常变动的,这样写也没有什么问题,但是假如需求变动,比如不仅要发送邮件,还得发送短信,那还这样写,那register接口会变得很复杂。

那应该如何简化呢?没错,就是观察者模式。

image-20210920221425379

5.2 观察者模式实现

我们直接套用 JDK 的实现。

import java.util.Observable;/*** 用户登录——被观察者*/
public class UserControllerObservable extends Observable {public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则通知所有观察者if(userId != null){super.setChanged();super.notifyObservers(userName);}}public Long saveUser(String userName, String passWord){return 1L;}}
import java.util.Observable;
import java.util.Observer;/*** 发送邮件——观察者*/
public class MailObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("发送邮件:" + arg + "欢迎你");}
}
/*** 发送手机短信——观察者*/
public class SMSObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("发送短信:" + arg + "欢迎你");}
}

测试:

public class UserClient {public static void main(String[] args) {UserControllerObservable observable = new UserControllerObservable();observable.addObserver(new MailObserver());observable.addObserver(new SMSObserver());observable.register("张三","123");}
}

image-20210920224858256

通过观察者模式改写后,后面用户注册,就算在增加别的操作,我们也只需要增加一个观察者即可,而注册接口 register 不会有任何改动。

5.3 异步模式优化

在回到前面那张图:

image-20210920221425379

注册之后进行的两步操作:发送邮件和发送短信,上面我们通过观察者模式改写之后,虽然流程很清晰,但是我们发现是顺序执行的,但其实这两步操作没有先后顺序,于是,我们可以改成异步模式,增加执行效率。

/*** 发送邮件——观察者*/
public class MailObserver implements Observer {private Executor executor = Executors.newFixedThreadPool(2);@Overridepublic void update(Observable o, Object arg) {executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("发送邮件:" + arg + "欢迎你");}});}
}

5、EventBus

翻译为“事件总线”,它提供了实现观察者模式的骨架代码。我们可以基于此框架,非常容易地在自己的业务场景中实现观察者模式,不需要从零开始开发。其中,Google Guava EventBus 就是一个比较著名的 EventBus 框架,它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式。

PS:Google Guava 是一个特别好用的工具包,里面的代码也都实现的比较优雅,大家感兴趣的可以研究研究源码。

https://github.com/google/guava

下面我们以上面的例子来说明如何使用 EventBus:

①、导如 Guava 包

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1.1-jre</version>
</dependency>

②、具体代码如下:

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;import java.util.List;
import java.util.concurrent.Executors;public class UserController {private EventBus eventBus;public UserController(){eventBus = new AsyncEventBus(Executors.newFixedThreadPool(2));}/*** 注意:泛型参数是 Object,而不是接口 Observer* @param observerList*/public void setObserverList(List<Object> observerList){for(Object observer : observerList){eventBus.register(observer);}}public void register(String userName, String passWord){// 1、根据用户名密码保存在数据库Long userId = saveUser(userName, passWord);// 2、如果上一步有结果则通知所有观察者if(userId != null){eventBus.post(userName);}}public Long saveUser(String userName, String passWord){return 1L;}
}
import com.google.common.eventbus.Subscribe;/*** 发送邮件——观察者*/
public class MailObserver{@Subscribepublic void sendMail(String userName) {System.out.println("发送邮件:" + userName + "欢迎你");}
}
import com.google.common.eventbus.Subscribe;/*** 发送手机短信——观察者*/
public class SMSObserver{@Subscribepublic void sendSMS(String userName) {System.out.println("发送短信:" + userName + "欢迎你");}
}

测试:

public class EventBusClient {public static void main(String[] args) {UserController userController = new UserController();List<Object> observerList = new ArrayList<>();observerList.add(new MailObserver());observerList.add(new SMSObserver());userController.setObserverList(observerList);userController.register("张三","123");}
}

利用 EventBus 框架实现的观察者模式,跟从零开始编写的观察者模式相比,从大的流程上来说,实现思路大致一样,都需要定义 Observer,并且通过 register() 函数注册 Observer,也都需要通过调用某个函数(比如,EventBus 中的 post() 函数)来给 Observer 发送消息(在 EventBus 中消息被称作事件 event)。但在实现细节方面,它们又有些区别。基于 EventBus,我们不需要定义 Observer 接口,任意类型的对象都可以注册到 EventBus 中,通过 @Subscribe 注解来标明类中哪个函数可以接收被观察者发送的消息。

6、观察者模式优点

①、观察者和被观察者之间是抽象耦合

不管是增加观察者还是被观察者都非常容易扩展,在系统扩展方面会得心应手。

②、建立一套触发机制

被观察者变化引起观察者自动变化。但是需要注意的是,一个被观察者,多个观察者,Java的消息通知默认是顺序执行的,如果一个观察者卡住,会导致整个流程卡住,这就是同步阻塞。

所以实际开发中没有先后顺序的考虑使用异步,异步非阻塞除了能够实现代码解耦,还能充分利用硬件资源,提高代码的执行效率。

另外还有进程间的观察者模式,通常基于消息队列来实现,用于实现不同进程间的观察者和被观察者之间的交互。

7、观察者模式应用场景

①、关联行为场景。

②、事件多级触发场景。

③、跨系统的消息交换场景, 如消息队列的处理机制。


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

相关文章

李喆:程序员到底怎么了

李喆&#xff1a;程序员到底怎么了 我们是这样的一群人&#xff1a;每天都在“努力”的工作着&#xff0c;每天都和计算机打交道&#xff0c;泡在网上&#xff0c;打游戏&#xff0c;查资料&#xff0c;发微博。可是有一天&#xff0c;突然意识到&#xff0c;我们的未来在哪里&…

Java~设计模式之观察者模式 || 班长大喊老师来啦

文章目录 观察者模式实现 观察者模式 定义: 当对象间存在一对多关系时&#xff0c;则使用观察者模式&#xff08;Observer Pattern&#xff09;。比如&#xff0c;当一个对象被修改时&#xff0c;则会自动通知依赖它的对象。观察者模式属于行为型模式。意图&#xff1a;定义对…

张小飞的Java之路——第九章

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 诸小亮&#xff1a;“封装说完后&#xff0c;我们接着看面向对象的第二大…

Java设计模式(2 / 23):观察者模式

定义 观察者&#xff08;Observer&#xff09;模式定义了对象之间的一对多依赖&#xff0c;这样一来&#xff0c;当一个对象改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新。 OO设计原则&#xff1a;为了交互对象之间的松耦合设计而努力。 案例&#xff1a;气…

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

前言 &#x1f34a;作者简介&#xff1a; 不肯过江东丶&#xff0c;一个来自二线城市的程序员&#xff0c;致力于用“猥琐”办法解决繁琐问题&#xff0c;让复杂的问题变得通俗易懂。 &#x1f34a;支持作者&#xff1a; 点赞&#x1f44d;、关注&#x1f496;、留言&#x1f4…

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…