设计模式 行为型 观察者模式(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的内部实现非常复杂,但基于上述原理,我们可以简化地理解其观察者模式的实现过程:
- 初始化数据:使用
Object.defineProperty()
为数据对象的每个属性设置getter和setter。 - 依赖收集:在getter函数中,检查当前是否存在活动的Watcher(通常是在组件渲染或计算属性计算时),如果存在,则将其添加到该属性的依赖列表中。
- 派发更新:在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
是观察者。当按钮被点击(主题状态改变)时,会触发观察者函数,这类似于观察者模式的通知机制。
六、应用场景
当一个对象的改变需要同时改变其他对象,而不知道具体有多少个对象有待改变时。
当一个抽象模型有两个方面,其中一个方面依赖于另一方面,这时可以通过观察者模式将这两者封装在独立的对象中以使它们各自独立地改变和复用。
当对一个对象的改变需要广播到其他对象时。
观察者模式广泛应用于各种需要通知多个对象进行同步更新的场合,包括但不限于:
- GUI事件监听机制:在图形用户界面编程中,按钮、文本框等控件的事件处理通常使用观察者模式。
- 数据模型与视图同步:在MVC架构中,观察者模式常用于数据模型和视图之间的更新同步。
- 发布-订阅系统:观察者模式是发布-订阅系统的基础,允许不同的服务订阅某个主题并接收通知。
- 股票价格监控:在金融系统中,观察者模式可以让股票价格的变化自动通知所有依赖该数据的系统。
- 社交媒体的通知机制:当用户发布新动态时,所有关注者都会收到通知。
七、优缺点
优点
- 解耦性高:主题和观察者之间是松耦合的关系。主题只需要知道观察者实现了更新方法,而不需要了解观察者的具体细节。这样可以方便地添加、删除和替换观察者,同时也使得主题和观察者可以独立地进行修改和扩展。
- 支持广播通信:主题可以同时通知多个观察者,使得系统能够方便地实现一对多的消息传递机制,提高了信息传播的效率。
缺点
- 可能导致性能问题:如果观察者数量过多,当主题状态发生变化时,通知所有观察者可能会消耗较多的时间和资源。特别是在一些对性能要求较高的场景下,可能需要对通知机制进行优化,比如采用异步通知等方式。
- 存在循环依赖风险:如果观察者在更新自己状态的过程中又对主题进行了操作,可能会导致循环依赖,使得系统的逻辑变得复杂,甚至出现死循环等问题。在设计和实现过程中需要特别注意避免这种情况。
相关文章:

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

【25考研】川大计算机复试情况,重点是啥?怎么准备?
24年进入复试的同学中,有10位同学的复试成绩为0分。具体是个人原因还是校方原因,还尚不明确。但是C哥提醒,一定要认真复习!复试完后不要跟任何人讨论有关复试的题目及细节! 一、复试内容 四川大学复试内容较多…...

C#调用Lua
目录 xLua导入 打包工具导入 单例基类导入与AB包管理器导入 Lua解析器 文件加载与重定向 Lua解析器管理器 全局变量获取 全局函数获取 对于无参数无返回值 对于有参数有返回值 对于多返回值 对于变长参数 完整代码 List与Dictionary映射Table 类映射Table 接口映射…...
LeetCode100之组合总和(39)--Java
1.问题描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复…...

NTN学习笔记之术语和缩写词解析
去发现,去努力,去表达。 参考:3GPP TR 38.811(R15),TR 38.821(R16) 目录 0. NTN典型架构图1. 术语2. 缩写 0. NTN典型架构图 为了方便对术语场景的理解,先放上两张NTN典…...
Yolo11改进:注意力改进|Block改进|ESSAformer,用于高光谱图像超分辨率的高效Transformer|即插即用
摘要 一、论文介绍 高光谱图像超分辨率的重要性:高光谱成像技术通过密集采样光谱特征,为材料区分提供丰富的光谱和空间结构信息,广泛应用于各领域。高光谱图像超分辨率(HSI-SR)旨在从低分辨率HSI生成高分辨率HSI。传统方法的局限性:传统方法依赖手工制作的先验,如低秩近…...

STM32 单片机 练习项目 LED灯闪烁LED流水灯蜂鸣器 未完待续
个人学习笔记 文件路径:程序源码\STM32Project-DAP&DAPmini\1-1 接线图 3-1LED闪烁图片 新建项目 新建项目文件 选择F103C8芯片 关闭弹出窗口 拷贝资料 在项目内新建3个文件夹 Start、Library、User Start文件拷贝 从资料中拷贝文件 文件路径:固…...
使用PyTorch实现基于稀疏编码的生成对抗网络(GAN)在CIFAR-10数据集上的应用
使用PyTorch实现基于稀疏编码的生成对抗网络(GAN)在CIFAR-10数据集上的应用 目录 使用PyTorch实现基于稀疏编码的生成对抗网络(GAN)在CIFAR-10数据集上的应用1. 引言2. 数据集介绍3. 模型网络结构3.1 网络结构3.2 编码器3.3 生成器3.4 判别器4. 模型优化器与损失函数4.1 优…...

用matlab调用realterm一次性发送16进制数
realterm采用PutString接口进行发送,需要注意的是发送的16进制数前面要加入0x标志。只有这样,realterm才能将输入的字符串识别为16进制数的形式。 另外,PutString函数支持两个参数输入,第一个参数为字符串,第二个参数为发送形式&…...

通过可穿戴外骨骼,以更灵活的方式操作你的机器人。
今天,我们将介绍一款专为控制 Mercury X1 和 Mercury B1 机械臂而设计的创新外骨骼。这种外骨骼以人类手臂的结构为蓝本,可实现直观和精确的控制。 开发这种外骨骼的动机源于人们对深度学习和机器学习等领域日益增长的兴趣。这些技术使机器人能够自主学习…...

记录将springboot的jar包和lib分离,使用docker-compose部署
本文讲诉如何把jar里的lib依赖包独立出来,方便更新服务时,缩小jar的体积,下面以若依的system服务为例,配置中的路径请酌情修改,主要提供大致配置逻辑 第一步:修改项目的pom.xml,调整build的配…...
JavaScript 延迟加载的方法
延迟加载(Lazy Loading)是一种优化网页性能的技术,它允许资源(如图片、脚本等)在需要时才被加载,而不是在页面初次加载时全部加载。这可以减少初始页面加载时间,提升用户体验,特别是…...
xrdp连接闪退情况之一
错误核查 首先使用命令vim ~/.xsession-errors,当里面的报错信息为WARNING **: Could not make bus activated clients aware of XDG_CURRENT_DESKTOPGNOME environment variable:Failed to execute child process “dbus-launch” (No such file or directory)&am…...

数据分析思维(八):分析方法——RFM分析方法
数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python,更重要的是数据分析思维。没有数据分析思维和业务知识,就算拿到一堆数据,也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》,本文内容就是提取…...

WebRTC 在视频联网平台中的应用:开启实时通信新篇章
在当今这个以数字化为显著特征的时代浪潮之下,实时通信已然稳稳扎根于人们生活与工作的方方面面,成为了其中不可或缺的关键一环。回首日常生活,远程办公场景中的视频会议让分散各地的团队成员能够跨越地理距离的鸿沟,齐聚一堂共商…...

Vue3(elementPlus) el-table替换/隐藏行箭头,点击整行展开
element文档链接: https://element-plus.org/zh-CN/component/form.html 一、el-table表格行展开关闭箭头替换成加减号 注:Vue3在样式中修改箭头图标无效,可能我设置不对,欢迎各位来交流指导 转变思路:隐藏箭头&…...

oracle闪回恢复数据:(闪回查询,闪回表,闪回库,回收站恢复)
oracle的闪回查询,可以查询提交在表空间的闪回数据,并可以还原所查询的数据,用于恢复短时间内的delele 或者 update 误操作,非常方便,缺点是只能恢复大概几小时内的数据。 文章目录 概要闪回查询恢复数据的主要方法包括…...

C语言——结构体,位段,枚举和联合
目录 前言 结构体 1含义 2语法 3匿名结构体 4结构体自引用 5结构体的定义与初始化 6内存对齐 7修改对齐数 8结构体传参 位段 1含义 2位段的内存分配 编辑3位段的问题 4位段的应用 枚举 1含义 2定义 3枚举优点 4枚举使用 联合 1含义 2定义 3特点 4计…...

期末概率论总结提纲(仅适用于本校,看文中说明)
文章目录 说明A选择题1.硬币2.两个事件的关系 与或非3.概率和为14.概率密度 均匀分布5.联合分布率求未知参数6.联合分布率求未知参数7.什么是统计量(记忆即可)8.矩估计量9.117页12题10.显著水平阿尔法(背公式就完了) 判断题11.事件…...
Python视频处理:噪声矩阵与并行计算的完美融合
噪声级别对视频质量有显著的影响,主要体现在以下几个方面: 1. 视觉质量 低噪声级别:当噪声级别较低时,视频的视觉质量较好。噪声对图像细节的干扰较小,画面看起来较为清晰和自然。观众可以更容易地识别图像中的细节和…...

老年生活照护实训室建设规划:照护质量评估与持续改进实训体系
随着人口老龄化程度的不断加深,老年生活照护需求日益增长,对专业照护人才的培养提出了更高要求。老年生活照护实训室建设方案作为培养高素质照护人才的重要载体,其核心在于构建科学完善的照护质量评估与持续改进实训体系。通过该体系的建设&a…...
WEB3技术重要吗,还是可有可无?
我从几个角度给你一个全面、理性、技术导向的回答: ✅ 一、Web3 技术的重要性:“有意义,但不是万能” Web3 技术并不是可有可无的噱头,而是一种在特定场景下提供独特价值的技术体系。 它重要的原因包括: 1. 重构数字…...

华为OD机考-内存冷热标记-多条件排序
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextInt();int[] arr new int[a];for(int…...

rknn优化教程(一)
文章目录 1. 前述2. 优化思想2.1 实时帧率2.2 多线程处理2.2.1 排序2.2.2 批量处理2.2.3 队列 2.3 进一步优化 3. 代码 1. 前述 OK,铺垫了很久的rknn优化,终于开始写了。为什么要优化呢?当然是我们的使用遇到了瓶颈,要么使用的时…...

FreeCAD:开源世界的三维建模利器
FreeCAD 开发模式 FreeCAD的开发采用多语言协作模式,其核心框架与高性能模块主要使用C构建,而用户界面与扩展功能则通过Python脚本实现灵活定制。具体来说: C核心层:作为基础架构,C负责实现与Open CASCADE Technology…...
指针的定义与使用
1.指针的定义和使用 int point1(){//定义指针int a 10;//指针定义语法: 数据类型 * 指针变量名int * p;cout << "sizeof (int(*)) --> " << sizeof(p) << endl;//让指针记录变量a的地址 & 取址符p &a ;cout << &qu…...
Nginx Stream 层连接数限流实战ngx_stream_limit_conn_module
1.为什么需要连接数限流? 数据库/Redis/MQ 连接耗资源:恶意脚本或误配可能瞬间占满连接池,拖垮后端。防御慢速攻击:层叠式限速(连接数+带宽)可阻挡「Slow Loris」之类的 TCP 低速洪水。公平接入…...

【深度学习新浪潮】如何入门三维重建?
入门三维重建算法技术需要结合数学基础、计算机视觉理论、编程实践和项目经验,以下是系统的学习路径和建议: 一、基础知识储备 1. 数学基础 线性代数:矩阵运算、向量空间、特征分解(用于相机矩阵、变换矩阵推导)。几何基础:三维几何(点、线、面的表示)、射影几何(单…...
【HarmonyOS 5】运动健康开发实践介绍以及详细案例
以下是 HarmonyOS 5 运动健康功能的简洁介绍,聚焦核心体验与技术亮点: 一、AI 驱动的全场景健康管理 智能运动私教:运动前推送热身指导,运动中实时纠正动作,运动后生成个性化报告与改进建议。AI 融合用户多设备数…...

9.进程间通信
1.简介 为啥要有进程间通信? 如果未来进程之间要协同呢?一个进程要把自己的数据交给另一个进程!进程是具有独立性的,所以把一个进程的数据交给另一个进程----基本不可能!必须通信起来,就必须要有另一个人…...