设计模式 行为型 观察者模式(Observer Pattern)与 常见技术框架应用 解析

在这里插入图片描述

观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

一、核心思想

观察者模式的核心思想是解耦主题(被观察者)和观察者,使得它们可以独立变化而不会相互影响。这有助于降低对象之间的耦合度,提高系统的可扩展性和可维护性。

二、定义与结构

1. 定义

观察者模式定义了对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

2. 结构

  • Subject(主题):也称为被观察者,它是一个接口或者抽象类,定义了添加、删除和通知观察者的方法。它维护了一个观察者列表,当自身状态发生变化时,通过遍历这个列表来通知所有的观察者。
  • ConcreteSubject(具体主题):实现了Subject接口,它包含了一些可以被观察的状态属性,并且在这些状态发生变化时,负责调用通知方法来通知观察者。
  • Observer(观察者):也是一个接口或者抽象类,它定义了一个更新方法,用于在收到主题通知时更新自己的状态。
  • ConcreteObserver(具体观察者):实现了Observer接口,它包含了用于存储自身状态的属性,并且实现了更新方法,在更新方法中根据主题传递过来的信息更新自己的状态。

三、角色

1. 主题(Subject)

  • 职责是管理观察者对象的引用列表,提供添加和删除观察者的方法。
  • 当自身状态发生变化时,负责通知所有已注册的观察者。

2. 观察者(Observer)

  • 定义了一个更新接口,当收到主题的通知时,会调用这个更新接口来更新自己的状态。

四、实现步骤及代码示例

1. 定义观察者接口

// 观察者接口
public interface Observer {void update(String news);
}

2. 定义主题接口

import java.util.ArrayList;
import java.util.List;// 主题接口
public interface Subject {void attach(Observer observer);void detach(Observer observer);void notifyObservers(String news);
}

3. 实现具体主题类

import java.util.ArrayList;
import java.util.List;// 具体主题类
public class NewsPublisher implements Subject {private List<Observer> observers = new ArrayList<>();private String latestNews;@Overridepublic void attach(Observer observer) {observers.add(observer);}@Overridepublic void detach(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String news) {this.latestNews = news;for (Observer observer : observers) {observer.update(latestNews);}}
}

4. 实现具体观察者类

// 具体观察者类
public class NewsSubscriber implements Observer {private String name;public NewsSubscriber(String name) {this.name = name;}@Overridepublic void update(String news) {System.out.println(name + " received news: " + news);}
}

5. 测试代码

public class Main {public static void main(String[] args) {NewsPublisher publisher = new NewsPublisher();NewsSubscriber subscriber1 = new NewsSubscriber("Subscriber 1");NewsSubscriber subscriber2 = new NewsSubscriber("Subscriber 2");publisher.attach(subscriber1);publisher.attach(subscriber2);publisher.notifyObservers("New technology released!");}
}

五、常见技术框架应用

1、Vue 2 数据劫持

在Vue 2中,观察者模式是通过Object.defineProperty()方法实现的,这允许Vue能够追踪数据对象属性的变化,并在变化发生时触发相应的更新。以下是对Vue 2中基于Object.defineProperty()实现的观察者模式的详细解析:

1.1. 数据劫持

Vue 2 使用Object.defineProperty()来对数据对象的属性进行劫持。这个方法允许我们定义一个对象属性的getter和setter函数,当属性被访问或修改时,这些函数会被调用。

Object.defineProperty(obj, 'property', {get: function() {// 当属性被访问时执行的代码},set: function(newValue) {// 当属性被修改时执行的代码}
});

在Vue中,当数据对象被创建时,Vue会使用Object.defineProperty()遍历对象的所有属性,并为它们设置getter和setter。这样,当这些属性在将来被访问或修改时,Vue就能够感知到。

1.2. 依赖收集

在getter函数中,Vue会进行依赖收集。所谓依赖收集,就是当某个属性被访问时,Vue会记录下当前正在执行的Watcher(观察者)。Watcher是Vue内部用于追踪数据变化的机制,它会在数据变化时触发相应的更新。

当组件渲染或计算属性被计算时,它们会访问数据对象的属性,这时getter函数就会被调用,Vue就会记录下当前的Watcher。

1.3. 派发更新

在setter函数中,当数据属性被修改时,Vue会触发setter函数。在这个函数中,Vue会遍历之前收集的依赖(Watcher),并通知它们数据已经发生了变化,需要重新执行以更新视图或计算属性。

1.4. 观察者(Watcher)

Watcher是Vue内部的一个类,它用于追踪数据的变化并在数据变化时执行相应的回调函数。在Vue中,Watcher有三种类型:

  • 渲染Watcher:与组件的渲染函数相关联,当数据变化时,它会重新渲染组件。
  • 计算属性Watcher:与计算属性相关联,当计算属性的依赖数据变化时,它会重新计算计算属性的值。
  • 侦听器Watcher:通过vm.$watch方法创建的Watcher,用于在数据变化时执行特定的回调函数。

1.5. 实现细节

Vue 2的内部实现非常复杂,但基于上述原理,我们可以简化地理解其观察者模式的实现过程:

  1. 初始化数据:使用Object.defineProperty()为数据对象的每个属性设置getter和setter。
  2. 依赖收集:在getter函数中,检查当前是否存在活动的Watcher(通常是在组件渲染或计算属性计算时),如果存在,则将其添加到该属性的依赖列表中。
  3. 派发更新:在setter函数中,当属性值发生变化时,遍历该属性的依赖列表,并通知每个依赖(Watcher)数据已经变化,需要执行更新操作。

1.6. 注意事项

  • 由于Object.defineProperty()只能对对象的已有属性进行劫持,因此Vue无法检测到对象属性的添加或删除。为了解决这个问题,Vue提供了Vue.set()Vue.delete()方法。
  • 深度监听:默认情况下,Vue只监听对象的第一层属性的变化。如果需要监听嵌套对象的变化,可以在创建Vue实例时通过observe选项开启深度监听(但请注意性能开销)。然而,在Vue 2中,深度监听通常是通过递归地为嵌套对象设置getter和setter来实现的,而不是简单地通过observe选项。实际上,observe选项在Vue 2中并不直接控制深度监听。
  • 响应式系统的局限性:由于JavaScript的限制和性能考虑,Vue的响应式系统有一些局限性。例如,它不能检测数组元素通过索引的直接修改或对象属性的添加/删除(除非使用Vue.set()/Vue.delete())。

综上所述,Vue 2通过Object.defineProperty()实现了观察者模式,使得Vue能够追踪数据对象属性的变化并在变化发生时自动更新视图。然而,由于JavaScript的限制和性能考虑,Vue的响应式系统有一些局限性需要注意。

2. JavaScript 中 事件监听

// 创建一个按钮元素
const button = document.createElement('button');
button.textContent = 'Click me';// 定义观察者函数(事件处理函数)
const observerFunction = function () {console.log('Button was clicked!');
};// 主题(按钮)添加观察者(事件监听)
button.addEventListener('click', observerFunction);// 将按钮添加到文档中
document.body.appendChild(button);

在这个JavaScript示例中,button是主题,observerFunction是观察者。当按钮被点击(主题状态改变)时,会触发观察者函数,这类似于观察者模式的通知机制。

六、应用场景

当一个对象的改变需要同时改变其他对象,而不知道具体有多少个对象有待改变时。
当一个抽象模型有两个方面,其中一个方面依赖于另一方面,这时可以通过观察者模式将这两者封装在独立的对象中以使它们各自独立地改变和复用。
当对一个对象的改变需要广播到其他对象时。

观察者模式广泛应用于各种需要通知多个对象进行同步更新的场合,包括但不限于:

  1. GUI事件监听机制:在图形用户界面编程中,按钮、文本框等控件的事件处理通常使用观察者模式。
  2. 数据模型与视图同步:在MVC架构中,观察者模式常用于数据模型和视图之间的更新同步。
  3. 发布-订阅系统:观察者模式是发布-订阅系统的基础,允许不同的服务订阅某个主题并接收通知。
  4. 股票价格监控:在金融系统中,观察者模式可以让股票价格的变化自动通知所有依赖该数据的系统。
  5. 社交媒体的通知机制:当用户发布新动态时,所有关注者都会收到通知。

七、优缺点

优点

  1. 解耦性高:主题和观察者之间是松耦合的关系。主题只需要知道观察者实现了更新方法,而不需要了解观察者的具体细节。这样可以方便地添加、删除和替换观察者,同时也使得主题和观察者可以独立地进行修改和扩展。
  2. 支持广播通信:主题可以同时通知多个观察者,使得系统能够方便地实现一对多的消息传递机制,提高了信息传播的效率。

缺点

  1. 可能导致性能问题:如果观察者数量过多,当主题状态发生变化时,通知所有观察者可能会消耗较多的时间和资源。特别是在一些对性能要求较高的场景下,可能需要对通知机制进行优化,比如采用异步通知等方式。
  2. 存在循环依赖风险:如果观察者在更新自己状态的过程中又对主题进行了操作,可能会导致循环依赖,使得系统的逻辑变得复杂,甚至出现死循环等问题。在设计和实现过程中需要特别注意避免这种情况。

在这里插入图片描述

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

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

相关文章

03_Redis基本操作

1.Redis查询命令 1.1 官网命查询命令 为了便于学习Redis,官方将其用于操作不同数据类型的命令进行了分类整理。你可以通过访问Redis官方网站上的命令参考页面https://redis.io/commands来查阅这些分组的命令,这有助于更系统地理解和使用Redis的各项功能。 1.2 HELP查询命令…

探索式测试

探索式测试是一种软件测试风格&#xff0c;它强调独立测试人员的个人自由和职责&#xff0c;为了持续优化其工作的价值&#xff0c;将测试学习、测试设计、测试执行和测试结果分析作为相互支持的活动&#xff0c;在整个项目实现过程中并行地执行。 选择合适的探索式测试方法我…

uniapp 微信小程序内嵌h5实时通信

描述&#xff1a; 小程序webview内嵌的h5需要向小程序实时发送消息&#xff0c;有人说postMessage可以实现&#xff0c;所以试验一下&#xff0c;结果是实现不了实时&#xff0c;只能在特定时机后退、组件销毁、分享时小程序才能接收到信息&#xff08;小程序为了安全等考虑做了…

php 使用simplexml_load_string转换xml数据格式失败

本文介绍如何使用php函数解析xml数据为数组。 <?php$a <xml><ToUserName><![CDATA[ww8b77afac71336111]]></ToUserName><FromUserName><![CDATA[sys]]></FromUserName><CreateTime>1736328669</CreateTime><Ms…

HOW - Form 表单 label 和 wrapper 对齐场景

一、背景 在日常使用 表单 时&#xff0c;我们一般有如下布局&#xff1a; 可以通过 Form 表单提供的配置直接设置&#xff1a; <Formform{form}labelCol{{ span: 4 }}wrapperCol{{ span: 20 }}onFinish{handleSubmit}><Form.Itemlabel"输入框"name"…

转运机器人在物流仓储行业的优势特点

在智能制造与智慧物流的浪潮中&#xff0c;一款革命性的产品正悄然改变着行业的面貌——富唯智能转运机器人&#xff0c;它以卓越的智能科技与创新的设计理念&#xff0c;引领着物流领域步入一个全新的高效、智能、无人的时代。 一、解放双手&#xff0c;重塑物流生态 富唯智能…

基于单片机的无线智能窗帘控制器的设计

摘 要 : 本文以单片机为控制核心 , 基于 PT2262/ 2272 无线收发模块 , 实现了窗帘的无线远程智能控制 . 该控制器通过高频无线收发模块实现了遥控窗帘的开合控制; 根据外部光线强弱实现自动开关窗帘 ; 根据设定时间自动完成开关过程; 通过语音播报当前环境温湿度信息以…

linux centos挂载未分配的磁盘空间

使用到的命令 lshw -class disk -short hostnamectl fdisk /dev/sdb partprobe /dev/sdb mount /dev/sdb2 /opt/fastdfs/ mkfs.ext4 /dev/sdb2 mount -t ext4 /dev/sdb2 /opt/fastdfs/

Vivado中Tri_mode_ethernet_mac的时序约束、分析、调整——(一)时序约束的基本概念

1、基本概念 推荐阅读&#xff0c;Ally Zhou编写的《Vivado使用误区与进阶》系列文章&#xff0c;熟悉基本概念、tcl语句的使用。 《Vivado使用误区与进阶》电子书开放下载&#xff01;&#xff01; 2、Vivado中的语法例程 1&#xff09;语法例程 约束的语句可以参考vivado…

基于Spring Boot的城市垃圾分类管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【YOLOv8杂草作物目标检测】

YOLOv8杂草目标检测 算法介绍模型和数据集下载 算法介绍 YOLOv8在禾本科杂草目标检测方面有显著的应用和效果。以下是一些关键信息的总结&#xff1a; 农作物幼苗与杂草检测系统&#xff1a;基于YOLOv8深度学习框架&#xff0c;通过2822张图片训练了一个目标检测模型&#xff…

比亚迪夏直插家用MPV腹地,“迪王”开启全面销冠新征程

文/王俣祺 导语&#xff1a;比亚迪前脚刚收获2024年的全面成功&#xff0c;后脚立刻就开始布局2025年的产品矩阵了。比亚迪夏的横空出世&#xff0c;看来家用MPV市场也要感受“迪王”的恐怖如斯了。 家用MPV市场的“意外之喜” 1月8日&#xff0c;比亚迪夏终于在万众瞩目之下…

探索数据存储的奥秘:深入理解B树与B+树

key value 类型的数据红黑树&#xff08;最优二叉树&#xff0c;内存最优&#xff09;&#xff0c;时间复杂度&#xff1a;O&#xff08;logn&#xff09;,调整方便&#xff1b;一个结点分出两个叉B树一个节点可以分出很多叉数据量相等的条件下&#xff1a;红黑树的层数很高&am…

联邦大语言模型典型系统: FATE - LLM、FedLLM、FederatedScope - LLM、PrimiHub

联邦大语言模型典型系统: FATE - LLM、FedLLM、FederatedScope - LLM、PrimiHub 目录 联邦大语言模型典型系统: FATE - LLM、FedLLM、FederatedScope - LLM、PrimiHubPEFT 技术及简单举例PEFT 技术代码实现提示词工程不仅仅在聊天对话框实现,还可以再代码中实现联邦大语言模…

L1G5000 XTuner 微调个人小助手认知

使用 XTuner 微调 InternLM2-Chat-7B 实现自己的小助手认知 1 环境配置与数据准备步骤 0. 使用 conda 先构建一个 Python-3.10 的虚拟环境步骤 1. 安装 XTuner 修改提供的数据步骤 0. 创建一个新的文件夹用于存储微调数据步骤 1. 创建修改脚本步骤 2. 执行脚本步骤 3. 查看数据…

网络协议安全的攻击手法

1.使用SYN Flood泛洪攻击&#xff1a; SYN Flood(半开放攻击)是最经典的ddos攻击之一&#xff0c;他利用了TCP协议的三次握手机制&#xff0c;攻击者通常利用工具或控制僵尸主机向服务器发送海量的变源端口的TCP SYN报文&#xff0c;服务器响应了这些报文后就会生成大量的半连…

Excel 技巧08 - 如何计算某类(比如红色背景色)单元格的总和? (★)

本文讲了如何在Excel中计算某类(比如红色背景色)单元格的总和。 1&#xff0c;如何计算某类(比如红色背景色)单元格的总和&#xff1f; 技巧就是先把它们给标记出来&#xff0c;然后就好统计了。 那么如何找出来呢&#xff1f; 对&#xff0c;就是通过红色。 按下Ctrl F 点…

uni-app无限级树形组件简单实现

因为项目一些数据需要树形展示&#xff0c;但是官网组件没有。现在简单封装一个组件在app中使用&#xff0c;可以无线嵌套&#xff0c;展开&#xff0c;收缩&#xff0c;获取子节点数据等。 简单效果 组件TreeData <template><view class"tree"><te…

互联网架构变迁:从 TCP/IP “呼叫” 到 NDN “内容分发” 的逐浪之旅

本文将给出关于互联网架构演进的一个不同视角。回顾一下互联网的核心理论基础产生的背景&#xff1a; 左边是典型的集中控制通信网络&#xff0c;很容易被摧毁&#xff0c;而右边的网络则没有单点问题&#xff0c;换句话说它很难被全部摧毁&#xff0c;与此同时&#xff0c;分…

移远BC28_opencpu方案_pin脚分配

先上图&#xff0c;BC28模块的pin脚如图所示&#xff1a; 下面看看GPIO的复用管脚 然后我自己整理了一份完整的pin功能列表