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

news2025/1/8 8:42:50

在这里插入图片描述

观察者模式(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;

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public 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;
    }

    @Override
    public 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.coloradmin.cn/o/2273111.html

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

相关文章

《Opencv》图像的旋转

一、使用numpy库实现 np.rot90(img,-1) 后面的参数为-1时事顺时针旋转&#xff0c;为1时是逆时针旋转。 import cv2 import numpy as np img cv2.imread(./images/kele.png) """方法一""" # 顺时针90度 rot_1 np.rot90(img,-1) # 逆时针90度…

Android Studio 安装配置(个人笔记)

Android studio安装的前提是必须保证安装了jdk1.8版本以上 一、查看是否安装jdk cmd打开命令行&#xff0c;输入java -version 最后是一个关键点 输入 javac &#xff0c;看看有没有相关信息 没有就下载jdk Android studio安装的前提是必须保证安装了jdk1.8版本以上 可以到…

spicy.signal 报错解决

报错&#xff1a; ImportError: cannot import name ‘kaiser’ from ‘scipy.signal’ 解决办法 找到import的位置&#xff1a;将 from scipy.signal import kaiser 修改为 from scipy.signal.windows import kaiser

学习threejs,导入AWD格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AWDLoader AWD模型加…

【Dify】Dify自定义模型设置 | 对接DMXAPI使用打折 Openai GPT 或 Claude3.5系列模型方法详解

一、Dify & DMXAPI 1、Dify DIFY&#xff08;Do It For You&#xff09;是一种自动化工具或服务&#xff0c;旨在帮助用户简化操作&#xff0c;减少繁琐的手动操作&#xff0c;提升工作效率。通过DIFY&#xff0c;用户能够快速完成任务、获取所需数据&#xff0c;并且可以…

5. CSS引入方式

5.1 CSS的三种样式 按照 CSS 样式书写的位置(或者引入的方式)&#xff0c;CSS样式表可以分为三大类&#xff1a; 1.行内样式表&#xff08;行内式&#xff09; 2.内部样式表&#xff08;嵌入式&#xff09; 3. 外部样式表&#xff08;链接式&#xff09; 5.2 内部样式表 …

大型语言模型(LLM)中的tokens是什么

大型语言模型(LLM)中的tokens是什么 在大型语言模型(LLM)中,tokens是文本处理的基本单位,它可以是一个单词、一个字符、一个标点符号,或者是一个特殊的标记。以下是关于tokens的详细介绍及举例: 一、tokens的定义和作用 定义:tokens是将文本分割成的一个个有意义的…

计算机网络 (29)网络地址转换NAT

前言 网络地址转换&#xff08;Network Address Translation&#xff0c;NAT&#xff09;是计算机网络中的一种重要协议&#xff0c;它主要用于将私有IP地址转换为公共IP地址&#xff0c;以实现内部网络与外部网络之间的通信。 一、基本概念 NAT是一种在局域网&#xff08;LAN&…

Node.js——fs(文件系统)模块

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

【cuda学习日记】2.1 2D matrix操作

2.1.1 检查块和线程索引 #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <cuda_runtime.h>#define CHECK(call) \{\const cudaError_t error call; \if (error ! cudaSuccess)\{\printf("Error…

Nginx:会话保持

会话保持 是指在负载均衡环境中,确保来自同一用户的多个请求都发送到同一个后端服务器。这通常用于那些需要记住用户状态或上下文的应用程序,例如购物车、登录状态等。 会话保持的重要性 用户体验:保证用户在整个会话期间的一致性体验,避免因不同服务器间的数据不同步导致…

Java-数据结构-链表-高频面试题(1)

在上一篇文章中&#xff0c;我们学习了链表中的"单向链表"&#xff0c;但学可不代表就是学会了&#xff0c;能够运用链表的地方比比皆是&#xff0c;解题方法也是层出不穷&#xff0c;今天就让我们巩固一下"单向链表"的知识吧~ 第一题&#xff1a;相交链表…

5. 多线程(3) --- synchronized

文章目录 前言1. 如何解决线程安全问题 [回顾]2. synchronized 关键字2.1. 示例2.2.对示例进行变化2.3 synchronized的其他写法2.4 synchronized的特性2.4.1 互斥2.4.2. 刷新内存2.4.3. 可重入 前言 前面我们通过在两个线程中共同对count进行加一操作&#xff0c;最后得到的结…

阿尔法linux开发板ping不通百度

我使用的阿尔法linux板子&#xff0c;发现按照《03【正点原子】I.MX6U网络环境TFTP&NFS搭建手册V1.3.2》一套操作下来&#xff0c;还是没办法实现板子上网。 我总结了下面方法&#xff0c;我如何实现联网和互ping通&#xff0c;大致总结下三步 一、pc端的wifi网络&#xf…

Qt之屏幕录制设计(十六)

Qt开发 系列文章 - screencap&#xff08;十六&#xff09; 目录 前言 一、实现原理 二、实现方式 1.创建录屏窗口 2.录屏窗口类定义 3.自建容器对象定义 4.用户使用 5.效果演示 总结 前言 利用Qt实现屏幕录制设计&#xff0c;可以通过使用Qt自带的类QScreen、QPixma…

AI通过数据构建一个独有对话机器人

AI通过数据构建一个独有对话机器人&#xff0c;尝试构建快速构建专有知识的机器人。 前端使用tinker实现一个简单的对话窗口&#xff0c; 后端使用自己的数据进行不断的训练&#xff0c;有需要的可以依据自己的实际情况进行修改&#xff0c;和优化 import tkinter as tk fro…

xml格式化(1):使用python的xml库实现自闭合标签

前言 最近一段时间一直想要写一个urdf格式化插件。 至于为什么嘛&#xff0c;因为使用sw2urdf插件&#xff0c;导出的urdf&#xff0c;同一标签的内容&#xff0c;是跨行的&#xff0c;这就导致&#xff0c;内容比较乱&#xff0c;而且行数比较多。影响阅读。 因此&#xff…

【免费】2004-2019年各省规模以上工业企业RD经费面板数据

2004-2019年各省规模以上工业企业R&D经费面板数据 1、时间&#xff1a;2004-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、规模以上工业企业R&D经费(万元) 4、范围&#xff1a;31省 5、规模以上工企&#xff0c…

电路学习(一)之电阻

电阻在电路中具有限制电流、分流、分压等功能&#xff0c;是电路中必不可少的组成部分。 1.什么是电阻&#xff1f; 电阻是一种符合欧姆定律&#xff08;R&#xff09;、限制电流流动的线性元件。简单来说&#xff0c;电阻就是可以限制电流流过的电子器件&#xff0c;其主要功…

Facebook元宇宙项目中的智能合约应用:提升虚拟空间的自治能力

近年来&#xff0c;Facebook在元宇宙领域的探索引起了广泛关注。元宇宙是一个融合虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;和互联网的沉浸式数字空间。在这个过程中&#xff0c;智能合约技术被认为是提升虚拟空间自治能力的关键工具。通过自动…