设计模式学习之——观察者模式

devtools/2024/11/30 5:19:53/

观察者模式是一种行为型设计模式,它用于在对象之间建立一对多的依赖关系。

一、定义与角色

  • 定义
    • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  • 主要角色
    • 主题(Subject):也称为被观察者,它维护一个观察者列表,并提供注册、删除和通知观察者的方法。
    • 观察者(Observer):对主题的状态变化感兴趣,并在状态变化时做出响应。它定义了一个更新方法,用于接收和处理主题的通知。
    • 具体主题(ConcreteSubject):实现了主题接口,维护了观察者的集合,并在状态变化时通知所有观察者。
    • 具体观察者(ConcreteObserver):实现了观察者接口,并对主题的状态变化做出具体的反应。

二、工作原理

  1. 注册观察者:观察者通过调用主题的注册方法,将自己添加到主题的观察者列表中。
  2. 状态变化通知:当主题的状态发生变化时,它会遍历观察者列表,并调用每个观察者对象的更新方法,将新的状态传递给观察者。
  3. 观察者响应:观察者对象接收到主题的通知后,执行相应的操作,以便响应状态的变化。

三、优缺点

优点: 

  1. 解耦性:主题和观察者之间是松耦合的,它们之间通过抽象的接口进行通信,使得它们可以独立地进行修改和扩展。
  2. 可扩展性:可以方便地增加新的观察者,而无需修改主题的代码。
  3. 灵活性观察者模式支持同步和异步通知,观察者可以在不同的线程中执行更新操作。
  4. 规范性观察者模式定义了主题和观察者之间的一套规范,使得代码更具可读性和可维护性。

缺点/注意事项:

  1. 循环依赖:避免在观察者和主题之间建立循环依赖关系,否则可能导致系统崩溃。
  2. 性能问题:当观察者数量非常多时,通知操作可能会非常耗时。可以考虑使用异步通知、事件总线等优化方法。
  3. 内存泄漏:如果观察者对象不再需要,确保从主题的观察者列表中删除它们,以避免内存泄漏。
  4. 线程安全:在多线程环境中,确保对观察者列表的访问是线程安全的。可以使用同步机制、并发集合等来实现。

四、应用场景

  1. 数据变化通知:当对象的状态发生变化,需要通知多个对象进行更新时,可以使用观察者模式。例如,股票市场的系统中,投资者需要实时获取股票价格的变化。
  2. 分布式事件系统:需要将事件从一个组件广播到多个组件时,可以使用观察者模式。例如,GUI框架中的事件处理机制,用户的操作需要通知多个界面组件。
  3. 系统状态监控:当系统的状态变化需要被监控时,可以使用观察者模式。例如,应用程序的日志系统、系统监控工具等。

五、示例代码

以下是一个简单的观察者模式示例代码,用于演示其工作原理:

// 主题接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 具体主题类
public class ConcreteSubject implements Subject {private List<Observer> observers = new LinkedList<>();private int state;public void setState(int state) {this.state = state;notifyObservers();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {observers.forEach(observer -> observer.update(state));}
}// 观察者接口
public interface Observer {void update(int state);
}// 具体观察者类
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(int state) {System.out.println(name + "接收到了更新请求,新的状态:" + state);}
}// 测试类
public class ObserverPatternExample {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("观察者1号");Observer observer2 = new ConcreteObserver("观察者2号");subject.registerObserver(observer1);subject.registerObserver(observer2);subject.setState(10); // 输出:观察者1号接收到了更新请求,新的状态:10;观察者2号接收到了更新请求,新的状态:10}
}

六、与发布-订阅模式的比较

观察者模式和发布-订阅模式都是软件设计模式,用于实现对象间的一对多依赖关系,但它们之间存在一些关键的区别。以下是对这两种模式的详细比较:

定义与特点
  • 观察者模式

    • 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
    • 主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
    • 观察者和主题之间是抽象耦合的,即它们之间通过接口或抽象类进行交互,而不是直接依赖具体实现。
  • 发布-订阅模式

    • 也称为发布/订阅模式或消息传递模式。
    • 在这种模式下,消息的发送者(发布者)和接收者(订阅者)不需要建立直接的联系,也不需要知道对方的存在。
    • 消息的传递通过一个被称为代理(Broker)或调度中心的中间角色来完成。发布者将消息发布到代理上,而订阅者则从代理订阅感兴趣的消息。
结构与角色
  • 观察者模式

    • Subject(主题/被观察者):维护一个观察者列表,提供注册、删除和通知观察者的方法。
    • Observer(观察者):定义了一个更新接口,用于接收来自主题的通知。
    • ConcreteSubject(具体主题):实现Subject接口,维护观察者列表,并在状态变化时调用所有观察者的更新方法。
    • ConcreteObserver(具体观察者):实现Observer接口,定义当接收到主题通知时的具体行为。
  • 发布-订阅模式

    • 发布者(Publisher):负责将消息发布到主题上。发布者一次只能向一个主题发送数据,且发布消息时无需关心订阅者是否在线。
    • 订阅者(Subscriber):通过订阅主题接收消息。订阅者可一次订阅多个主题,从而接收感兴趣的消息。
    • 代理(Broker):负责所有消息的路由和分发工作。代理接收到发布者的消息后,根据主题将其分发给相应的订阅者。
    • 主题(Topic):用于标识消息的类别。每个消息都包含一个主题,订阅者通过订阅主题来接收感兴趣的消息。
区别与联系
  • 区别

    • 耦合性观察者模式中,发布者与订阅者直接关联,发布者维护观察者列表,发布者状态变更通知订阅者。而在发布-订阅模式中,订阅者与发布者相互不了解,通过事件中心(代理)进行通信,实现了完全松耦合。
    • 角色数量观察者模式中只有两个主要角色:观察者和被观察者。而发布-订阅模式中则包含四个角色:发布者、订阅者、代理和主题。
    • 应用场景观察者模式多用于单个应用内部,而发布-订阅模式则更多的是一种跨应用的模式(cross-application pattern),如消息中间件等。
  • 联系

    • 两者都实现了对象间的一对多依赖关系,当某个对象的状态发生变化时,都会通知所有依赖的对象。
    • 两者都关注对象之间的通讯,并致力于保持对象间的低耦合和高协作性。

综上所述,观察者模式和发布-订阅模式在定义、结构、耦合性、角色数量和应用场景等方面都存在明显的区别。选择哪种模式取决于具体的应用需求和设计考虑。

最后,观察者模式是一种强大的设计模式,它能够在对象之间建立一对多的依赖关系,并自动通知观察者进行更新。然而,在实际应用中需要注意其潜在的性能问题和循环依赖问题


http://www.ppmy.cn/devtools/138105.html

相关文章

Linux 常用命令大全与详细讲解

Linux 作为一种流行的操作系统&#xff0c;广泛应用于服务器、开发环境和日常桌面使用中。Linux 的强大之处在于它的命令行工具&#xff0c;用户通过命令行可以执行系统管理、文件处理、网络配置等多种操作。本文将详细介绍一些 Linux 中最常用的命令&#xff0c;从文件操作到系…

深入讲解Spring Boot和Spring Cloud,外加图书管理系统实战!

很抱歉&#xff0c;我的疏忽&#xff0c;说了这么久还没有给大家详细讲解过Spring Boot和Spring Cloud,那今天给大家详细讲解一下。 大家可以和下面这三篇博客一起看&#xff1a; 1、Spring Boot 和 Spring Cloud 微服务开发实践详解https://blog.csdn.net/speaking_me/artic…

构建 LLM (大型语言模型)应用程序——从入门到精通(第七部分:开源 RAG)

通过检索增强生成 (RAG) 应用程序的视角学习大型语言模型 (LLM)。 本系列博文 简介数据准备句子转换器矢量数据库搜索与检索大语言模型开源 RAG&#xff08;本帖&#xff09;评估服务LLM高级 RAG 1. 简介 我们之前的博客文章广泛探讨了大型语言模型 (LLM)&#xff0c;涵盖了其…

Jenkins-Git Parameter 插件实现指定版本的发布和回滚

在上一篇文章的基础设置上进行 1. 机器准备 开发10.0.0.204gitlab10.0.0.201jenkins10.0.0.200web10.0.0.202 2. 开发主机 在开发机器上修改不同版本的前端页面&#xff0c;并打上标签 第一次修改 [rootdev wheel]#vim index.html [rootdev wheel]#git commit -am "1…

c++设计模式模块与系统

c 中lambda 本质就是一个匿名(没有名)的函数&#xff1b; 可以用一个数组元素存储一个函数的指针&#xff1b; 通过数组下标来使用函数&#xff1b; 高内聚低耦合 如何理解设计模式中的高内聚低耦合 高内聚: 用于指导如何组织和划分软件设计。 **定义&#xff1a;**高内聚指的…

通信与网络安全之IPSEC

IPSec&#xff08;IP Security&#xff09;是IETF制定的为保证在Internet上传送数据的安全保密性能的三层隧道加密协议。IPSec在网络层对IP报文提供安全服务。IPSec协议本身定义了如何在IP数据包中增加字段来保证IP包的完整性、 私有性和真实性&#xff0c;以及如何加密数据包。…

微信小游戏/抖音小游戏SDK接入踩坑记录

文章目录 前言问题记录1、用是否存在 wx 这个 API 来判断是微小平台还是抖小平台不生效2、微小支付的参数如何获取?3、iOS 平台不支持虚拟支付怎么办?微小 iOS 端支付时序图:抖小 iOS 端支付:4、展示广告时多次回调 onClose5、在使用单例时 this 引起的 bug6、使用 fetch 或…

前后端中Json数据的简单处理

很多时候因为数据库中不想创建中间表去存一些数据&#xff0c;所以在一个实体表中用一个json字段去存储并处理这些数据。本人倾向在前端js部分直接处理json数据&#xff0c;后端只做存储&#xff0c;不做处理。 前端在获取表单的时候解析 toEdit(row) {this.editForm { ...ro…