更灵活的对象之间的联动 - 观察者模式(Observer Pattern)

观察者模式(Observer Pattern)

  • 观察者模式(Observer Pattern)
    • 观察者模式(Observer Pattern)概述
      • 观察者模式(Observer Pattern) 结构图
      • 观察者模式(Observer Pattern)涉及的角色
    • talk is cheap, show you my code
    • 总结

观察者模式(Observer Pattern)

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者对象都会收到通知并自动更新。这种模式特别适用于构建事件处理系统、订阅-发布机制等场景。

还是举个现实中的例子
还记得我们都在使用手机打电话的时候吗? 那个时候没有进入移动互联网,我们需要给谁打电话就需要自己把它们的电话存储到我们的手机上去。这导致了一个什么问题呢? 我们需要的服务很多,但是手机存储很多电话很不方便。 于是有了114查号台,当我们想知道某一些电话的时候,可以直接打电话给114查号台,这个查号台存储了各种各样的电话,它们把自己的信息注册到查号台,我们直接跟查号台沟通就可以得到这些信息。 这种就是我们本次要介绍的观察者模式(Observer Pattern)。

观察者模式(Observer Pattern)概述

观察者模式(Observer Pattern) 结构图

在这里插入图片描述

观察者模式(Observer Pattern)涉及的角色

  1. 主题(Subject):主题接口声明了添加、删除和通知观察者的方法。它可以是接口或抽象类,具体主题实现类会实现这些方法,并维护观察者的列表。
import java.util.ArrayList;
import java.util.List;

public abstract class Subject {
    private final List<Observer> observers = new ArrayList<>();

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

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

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
  1. 具体主题(ConcreteSubject):具体主题实现了Subject接口,并包含有状态信息。当状态发生变化时,它会通知所有注册的观察者。
public class ConcreteSubject extends Subject {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        notifyObservers(); // 状态改变后通知所有观察者
    }
}
  1. 观察者(Observer):具体观察者实现了Observer接口,并在update()方法中定义了接收到通知后的具体行为。每个具体观察者都持有一个对具体主题的引用,以便它可以查询主题的状态。
public class ConcreteObserver implements Observer {
    private final ConcreteSubject subject;
    private String name;

    public ConcreteObserver(ConcreteSubject subject, String name) {
        this.subject = subject;
        this.name = name;
    }

    @Override
    public void update() {
        System.out.println(name + " received update: " + subject.getState());
    }
}

talk is cheap, show you my code

按照惯例,我们还是利用本节要介绍的设计模式来实现我们前面说的那个例子。

  1. 定义主题接口
    首先,我们定义一个Subject接口,它声明了添加、删除和通知观察者的方法。
import java.util.ArrayList;
import java.util.List;

public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}
  1. 实现具体主题
    然后,我们创建一个具体的主题类PhoneDirectory,它实现了Subject接口,并维护了一个电话号码列表。当电话号码发生变化时,它会通知所有注册的观察者。
public class PhoneDirectory implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private String phoneNumber;

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

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(phoneNumber);
        }
    }

    // 更新电话号码并通知观察者
    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        System.out.println("Phone number updated to: " + phoneNumber);
        notifyObservers();
    }
}
  1. 定义观察者接口
    接下来,我们定义一个Observer接口,它声明了一个update()方法,所有具体的观察者都需要实现这个方法以响应来自主题的通知。
public interface Observer {
    void update(String phoneNumber);
}
  1. 实现具体观察者
    现在,我们创建两个具体观察者类:DisplayBoard 和 MobileApp,它们实现了Observer接口,并在update()方法中定义了接收到通知后的具体行为。
public class DisplayBoard implements Observer {
    private final String name;

    public DisplayBoard(String name) {
        this.name = name;
    }

    @Override
    public void update(String phoneNumber) {
        System.out.println(name + " received update: Phone Number is " + phoneNumber);
    }
}

public class MobileApp implements Observer {
    private final String name;

    public MobileApp(String name) {
        this.name = name;
    }

    @Override
    public void update(String phoneNumber) {
        System.out.println(name + " received update: Phone Number is " + phoneNumber);
    }
}
  1. 客户端调用
    最后,在客户端代码中,我们将创建一个PhoneDirectory实例作为查号台,并为它添加一些观察者。然后,我们将模拟电话号码的更新过程。
public class Client {
    public static void main(String[] args) {
        // 创建查号台
        PhoneDirectory phoneDirectory = new PhoneDirectory();

        // 创建观察者
        Observer displayBoard = new DisplayBoard("Display Board");
        Observer mobileApp = new MobileApp("Mobile App");

        // 注册观察者到查号台
        phoneDirectory.attach(displayBoard);
        phoneDirectory.attach(mobileApp);

        // 模拟电话号码更新
        phoneDirectory.setPhoneNumber("123-456-7890"); // 第一次更新
        phoneDirectory.setPhoneNumber("098-765-4321"); // 第二次更新

        // 移除一个观察者
        phoneDirectory.detach(mobileApp);

        // 再次更新电话号码,此时只有displayBoard会收到通知
        phoneDirectory.setPhoneNumber("111-222-3333");
    }
}

总结

观察者模式的优点

  • 降低耦合度:观察者与主题之间的依赖是松散的,观察者只需要知道主题提供的接口,而不必了解主题的具体实现。
  • 支持广播通信:一个主题可以通知多个观察者,而且这些观察者之间相互独立,互不影响。
  • 易于扩展:新增加观察者非常容易,只需创建新的观察者实例并将其注册到主题即可,无需修改现有代码。
  • 符合开闭原则:系统可以在不改变原有代码的情况下引入新的功能(如新的观察者),这符合面向对象设计中的开闭原则(Open/Closed Principle)。

观察者模式的缺点:

  • 如果一个观察目标下面有很多观察者,通知所有观察者本身就会花费很多时间
  • 有可能在观察者与观察目标之前存在循环依赖的问题

观察者模式的应用场景

  • 事件驱动系统:如GUI框架中的按钮点击事件、窗口关闭事件等,观察者模式非常适合用来实现事件监听器。
  • 订阅-发布机制:例如新闻网站的用户订阅服务,每当有新文章发布时,所有订阅该频道的用户都会收到通知。
  • 缓存失效通知:在一个分布式系统中,当某个缓存项过期或被更新时,它可以通知所有依赖于该项的组件。
  • 游戏开发:玩家在游戏中完成任务或达到特定条件时,触发一系列事件,如获得奖励、解锁新内容等。

观察者模式提供了一种有效的方式来解耦对象之间的依赖,使得一个对象的状态变化可以自动通知到其他相关对象。它特别适用于那些需要实现事件处理、订阅-发布机制等情况。观察者涉及模式是一种使用频率非常高的设计模式,它主要是为对象之间的联动提供了一套解决方案。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Webpack 5 混淆插件terser-webpack-plugin生命周期作用时机和使用注意事项

参考案例代码 海南酷森科技有限公司/webpack-simple-demo Terser&#xff08;简要的/简短的&#xff09; 混淆依据 混淆是发生在代码已经 bundle 之后的事情 变量或者函数在被引用或赋值时才能被混淆 孤立的函数或者变量可能会被移除&#xff0c;但不会被混淆&#xff0c;要…

‌OCP英文全称是什么

在数据库领域&#xff0c;OCP全称为Oracle Certified Professional&#xff0c;是Oracle公司提供的Oracle数据库中级认证&#xff0c;专门针对数据库管理员(Database Administrator&#xff0c;简称DBA)和数据库开发人员。以下是关于OCP认证的详细介绍&#xff1a; 认证领域与…

MyBatis实现数据库的CRUD

本文主要讲解使用MyBatis框架快速实现数据库中最常用的操作——CRUD。本文讲解的SQL语句都是MyBatis基于注解的方式定义的&#xff0c;相对简单。 Mybatis中#占位符和$拼接符的区别 “#”占位符 在使用MyBatis操作数据库的时候&#xff0c;可以直接使用如下SQL语句删除一条数…

微调神经机器翻译模型全流程

MBART: Multilingual Denoising Pre-training for Neural Machine Translation 模型下载 mBART 是一个基于序列到序列的去噪自编码器&#xff0c;使用 BART 目标在多种语言的大规模单语语料库上进行预训练。mBART 是首批通过去噪完整文本在多种语言上预训练序列到序列模型的方…

RTX 5090 加持,科研服务器如何颠覆 AI 深度学习构架?

RTX 5090作为英伟达旗舰级GPU&#xff0c;凭借Ada Lovelace架构&#xff0c;融合创新的SM多单元流处理器、第三代RT Core与第四代Tensor Core&#xff0c;打造出极为强劲的计算体系。其24GB GDDR6X显存搭配1TB/s带宽&#xff0c;能以极低延迟和超高吞吐量处理大规模张量数据&am…

【2025最新】机器学习类计算机毕设选题80套,适合大数据,人工智能

【2025最新】机器学习类型计算机毕设选题 1-10套 基于Spring Boot的物流管理系统的设计与实现 基于机器学习的虚假招聘信息的分析与预测 基于机器学习的影响数据科学家职业变动因素的分析与预测 基于Spring Boot的历史文物交流平台的设计与实现 基于机器学习的肥胖影响因素的分…

【PPTist】幻灯片放映

放映功能的代码都在 src/hooks/useScreening.ts&#xff0c;我们看一下 从当前页开始 放映的功能。 // 进入放映状态&#xff08;从当前页开始&#xff09; const enterScreening () > {enterFullscreen()screenStore.setScreening(true) }首先是 enterFullscreen()&#…

MySQL 16 章——变量、流程控制和游标

一、变量 在MySQL数据库的存储过程和存储函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终的结果数据 在MySQL数据库中&#xff0c;变量分为系统变量和用户自定义变量 &#xff08;1&#xff09;系统变量 1.1.1系统变量分类 变量由…

T-SQL编程

目录 1、T-SQL的元素 1.1 标识符 1. 常规标识符 2. 分隔标识符 1.2 变量 1. 全局变量 2. 局部变量 1.3 运算符 1. 算数运算符 2. 赋值运算符 3. 位运算符 4. 比较运算符 5. 逻辑运算符 6. 字符串连接运算符 7. 一元运算符 8. 运算符的优先级和结合性 1.4 批处…

2024 China Collegiate Programming Contest (CCPC) Zhengzhou Onsite 基础题题解

L. Z-order Curve 思路&#xff1a;这题目说了&#xff0c;上面那一行&#xff0c;只有在偶数位才有可能存在1&#xff0c;那么一定存在这样的数&#xff0c;0 ,1,100, 10000,那么反之&#xff0c;我们的数列是行的二倍&#xff0c;因此会出现10,1000,100000这样的数&#xff0…

Unity2D初级背包设计后篇 拓展举例与不足分析

Unity2D初级背包设计中篇 MVC分层撰写(万字详解)-CSDN博客、 如果你已经搞懂了中篇&#xff0c;那么对这个背包的拓展将极为简单&#xff0c;我就在这里举个例子吧 目录 1.添加物品描述信息 2.拓展思路与不足分析 1.没有删除只有丢弃功能&#xff0c;所以可以添加垃圾桶 2.格…

vue(七) vue进阶

目录 第一课&#xff1a;Vue方法、计算机属性及侦听器 一、数组变化侦测 方法1&#xff1a;变更方法 方法2&#xff1a;替换一个数组 例子&#xff1a;小Demo:合并两个数组 二、计算属性 1.基础&#xff08;不推荐&#xff09; 2.使用计算属性来完成案例 3.使用函数的方…

Spring Boot 2 学习指南与资料分享

Spring Boot 2 学习资料 Spring Boot 2 学习资料 Spring Boot 2 学习资料 在当今竞争激烈的 Java 后端开发领域&#xff0c;Spring Boot 2 凭借其卓越的特性&#xff0c;为开发者们开辟了一条高效、便捷的开发之路。如果你渴望深入学习 Spring Boot 2&#xff0c;以下这份精心…

YangQG 面试题汇总

一、交叉链表 问题&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 解题思想&#xff1a; 双指针 备注&#xff1a;不是快慢指针&#xff0c;如果两个长度相…

fastapi 使用

参考&#xff1a; https://fastapi.tiangolo.com/zh/tutorial/first-steps/https://fastapi.tiangolo.com/zh/tutorial/first-steps/ FastAPI 用于基于标准 Python 类型提示使用 Python 构建 API&#xff0c;使用 ASGI 的标准来构建 Python Web 框架和服务器。所有简单理解&a…

2024年度漏洞态势分析报告,需要访问自取即可!(PDF版本)

2024年度漏洞态势分析报告&#xff0c;需要访问自取即可!(PDF版本),大家有什么好的也可以发一下看看

泛目录和泛站有什么差别

啥是 SEO 泛目录&#xff1f; 咱先来说说 SEO 泛目录是啥。想象一下&#xff0c;你有一个巨大的图书馆&#xff0c;里面的书架上摆满了各种各样的书&#xff0c;每一本书都代表着一个网页。而 SEO 泛目录呢&#xff0c;就像是一个超级图书管理员&#xff0c;它的任务就是把这些…

k8s基础(6)—Kubernetes-存储

Kubernetes-存储概述 k8s的持久券简介 Kubernetes的持久卷&#xff08;PersistentVolume, PV&#xff09;和持久卷声明&#xff08;PersistentVolumeClaim, PVC&#xff09;为用户在Kubernetes中使用卷提供了抽象。PV是集群中的一块存储&#xff0c;PVC是对这部分存储的请求。…

深度学习-卷积神经网络反向传播梯度公式推导

这篇文章非常棒&#xff0c;单样本单通道的反向传播梯度公式推导我都理解了。为了防止找不到原网页&#xff0c;所以特复制于此 参考&#xff1a; https://zhuanlan.zhihu.com/p/640697443

论文笔记(四十七)Diffusion policy: Visuomotor policy learning via action diffusion(下)

Diffusion policy: Visuomotor policy learning via action diffusion&#xff08;下&#xff09; 文章概括5. 评估5.1 模拟环境和数据集5.2 评估方法论5.3 关键发现5.4 消融研究 6 真实世界评估6.1 真实世界Push-T任务6.2 杯子翻转任务6.3 酱汁倒入和涂抹任务 7. 实际双臂任务…