【行为型模式】观察者模式

news2024/12/25 10:14:09

文章目录

    • 1、概述
    • 2、结构
    • 3、实现方式
      • 3.1、案例引入
      • 3.2、结构分析
      • 3.3、具体实现
    • 4、观察者模式优缺点
    • 5、应用场景

1、概述

观察者模式(Observer)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,其他所有依赖于它的对象都会自动被通知并更新。这个模式也被称为 发布/订阅模式

观察者模式通常用于需要实现事件驱动系统的场景中。例如,当用户订阅了一个新闻网站的推送服务后,当这个网站发布了新的文章时,它会将这些更新通知给所有的订阅用户。在这个例子中,订阅用户是观察者,新闻网站是被观察者。

观察者模式的好处是减少了对象之间的耦合性,使得它们可以独立地进行修改和扩展。同时,当我们需要为一个对象添加或删除观察者时,也变得更加容易。因此,观察者模式是一种非常有用的设计模式。

2、结构

观察者模式的结构通常由以下几个部分组成:

  1. Subject(目标):被观察者,它包含了所有的观察者,并提供了添加、删除和通知观察者的方法;
  2. Observer(观察者):观察者,它定义了接收目标发送的通知并更新自己状态的方法;
  3. ConcreteSubject(具体目标):具体的被观察者,它维护着一组观察者,并在状态发生变化时通知它们;
  4. ConcreteObserver(具体观察者):具体的观察者,实现了Observer定义的接口,以便在接收到通知时更新自己的状态。

uml图

3、实现方式

3.1、案例引入

当微信公众号发布一条新的文章时,所有关注该公众号的微信用户都会收到推送通知。在这个例子中,微信公众号就是被观察者,而所有关注该公众号的微信用户则是观察者。 当微信公众号发布新的文章时,它会将这个状态变化(即发布了新的文章)通知给所有观察者,也就是微信用户,从而让他们第一时间了解到最新的内容。

image-20230416133718647

3.2、结构分析

根据案例中的场景分析,可以将场景中的各个部分对应到观察者模式中的相应角色:

  1. Subject接口对应抽象被观察者(Subject)角色。
  2. WeChatOfficialAccount类对应具体被观察者(ConcreteSubject)角色。
  3. Observer接口对应抽象观察者(Observer)角色。
  4. WeChatUser类对应具体观察者(ConcreteObserver)角色。

案例uml

3.3、具体实现

以下是使用Java语言实现观察者模式的具体代码,实现微信公众号发布新文章通知所有关注该公众号的微信用户:

  1. 定义被观察者接口Subject和观察者接口Observer,其中Subject接口定义了三个方法:注册观察者、移除观察者和通知所有观察者;Observer接口定义了一个方法:更新状态。
// 定义被观察者接口
interface Subject {
    // 注册观察者
    void registerObserver(Observer observer); 
    // 移除观察者
    void removeObserver(Observer observer); 
    // 通知所有观察者
    void notifyObservers(String articleTitle); 
}

// 定义观察者接口
interface Observer {
    void update(String articleTitle); // 更新状态
}
  1. 微信公众号类WeChatOfficialAccount实现了被观察者接口Subject。它维护了一个观察者列表,实现了注册观察者、移除观察者和通知所有观察者的方法,并在发布新文章时通知所有观察者。
// 微信公众号类作为被观察者,实现Subject接口
class WeChatOfficialAccount implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String articleTitle;

    // 注册观察者
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    // 移除观察者
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    // 通知所有观察者
    @Override
    public void notifyObservers(String articleTitle) {
        for (Observer observer : observers) {
            observer.update(articleTitle);
        }
    }

    // 发布新文章,通知所有观察者
    public void publishArticle(String articleTitle) {
        this.articleTitle = articleTitle;
        System.out.println("微信公众号发布了新文章:" + articleTitle);
        notifyObservers(articleTitle);
    }
}
  1. 微信用户类WeChatUser实现了观察者接口Observer。它在接收到微信公众号发布新文章的通知时,更新状态,即打印接收到的文章标题。
// 微信用户类作为观察者,实现Observer接口
class WeChatUser implements Observer {
    private String userName;

    public WeChatUser(String userName) {
        this.userName = userName;
    }

    // 更新状态,即打印接收到的文章标题
    @Override
    public void update(String articleTitle) {
        System.out.println(userName + "收到了新文章通知:" + articleTitle);
    }
}
  1. 测试代码TestObserverPattern中,首先创建了一个微信公众号对象和三个微信用户对象,并让这三个用户都关注了该公众号。然后,微信公众号发布新文章,通知所有关注该公众号的微信用户。接着,其中一个微信用户取消了对该公众号的关注。最后,微信公众号再次发布新文章,通知所有关注该公众号的微信用户。
public class TestObserverPattern {
    public static void main(String[] args) {
        // 创建微信公众号对象
        WeChatOfficialAccount officialAccount = new WeChatOfficialAccount(); 

        // 创建微信用户对象
        WeChatUser zhangsan = new WeChatUser("张三"); 
        WeChatUser lisi = new WeChatUser("李四");
        WeChatUser wangwu = new WeChatUser("王五");

        // 微信用户关注微信公众号
        officialAccount.registerObserver(zhangsan); 
        officialAccount.registerObserver(lisi);
        officialAccount.registerObserver(wangwu);

        // 微信公众号发布新文章
        officialAccount.publishArticle("观察者模式在Java中的应用"); 

        // 微信用户李四取消关注微信公众号
        officialAccount.removeObserver(lisi); 

        // 微信公众号再次发布新文章
        officialAccount.publishArticle("学习Java多线程编程"); 
    }
}

整个程序运行后,输出结果为:

微信公众号发布了新文章:观察者模式在Java中的应用
张三收到了新文章通知:观察者模式在Java中的应用
李四收到了新文章通知:观察者模式在Java中的应用
王五收到了新文章通知:观察者模式在Java中的应用

微信公众号发布了新文章:学习Java多线程编程
张三收到了新文章通知:学习Java多线程编程
王五收到了新文章通知:学习Java多线程编程

4、观察者模式优缺点

观察者模式的优点:

  1. 降低了系统耦合度:被观察者对象和观察者对象之间是松耦合关系,它们之间仅仅通过接口进行交互,可以降低系统中各个对象之间的耦合度,提高系统的可扩展性和可维护性;

  2. 支持广播通信:一旦被观察者对象发生变化,它会自动通知所有观察者对象,从而实现了广播通信;

  3. 符合开闭原则:当需要在系统中添加新的观察者时,只需要定义一个新的观察者类并实现抽象观察者接口即可,不需要修改原有代码,符合开闭原则;

  4. 可以建立多层次触发链:观察者对象除了可以观察被观察者对象外,还可以是其他观察者对象的被观察者对象,从而实现多层次触发链。

观察者模式的缺点:

  1. 观察者太多,通知开销大:当观察者对象数量较多时,被观察者对象每次状态改变时都需要通知所有观察者对象,这将导致通知开销很大;

  2. 循环依赖问题:当观察者对象之间存在循环依赖时,系统的运行可能会出现问题;

  3. 无法保证通知顺序:由于被观察者和观察者之间是松耦合关系,观察者对象的调用顺序不能保证。

优点缺点
降低系统耦合度观察者太多,通知开销大
支持广播通信循环依赖问题
符合开闭原则无法保证通知顺序
可以建立多层次触发链

与此同时,针对观察者模式的缺点,可以采取以下优化的手段:

  1. 减少通知开销:如果被观察者状态变化太频繁,可以考虑采用“节流”或“防抖”的方式减少通知次数。例如,当被观察者状态连续变化时,只在最后一次变化后通知所有观察者;

  2. 避免循环依赖:可以通过定义一个中间对象来解决循环依赖问题,即让观察者对象与中间对象建立观察者-被观察者关系,中间对象与被观察者对象建立观察者-被观察者关系,从而避免存在直接的循环依赖;

  3. 确保通知顺序:可以在具体被观察者类中增加一个排序列表,每个观察者对象在注册时指定一个排序号,被观察者对象通知观察者时按照排序号的大小进行通知;

  4. 采用异步通知:将观察者的更新操作放到异步任务队列中执行,避免阻塞主线程,提高系统的响应速度和稳定性;

  5. 使用事件总线框架:可以使用事件总线框架来处理观察者模式中的通知问题,事件总线框架可以将观察者模式中的通知转化为事件,并在事件总线上进行管理和分发,从而实现了更高效的消息传递机制。

5、应用场景

观察者模式适用于以下场景:

  1. 当需要将一个对象的状态变化通知给其他多个对象时,可以使用观察者模式;

  2. 当一个对象的改变需要同时改变其他对象的状态或者操作时,可以使用观察者模式;

  3. 一个抽象模型有两个方面,其中一个方面依赖于另一个方面时,可以使用观察者模式。例如,模型和视图之间的关系,模型驱动视图的更新,而视图反过来也要反映到模型中;

  4. 当一个对象必须通知其他对象,但是你不知道这些对象是谁时,可以使用观察者模式。例如,你不知道哪些用户订阅了你的微信公众号,但你需要在每次发布新文章时通知所有订阅用户;

  5. 当一个对象需要有别的对象来协助完成某项工作,并且不希望知道具体协助它的对象时,可以使用观察者模式。例如,一个员工完成某项任务需要得到经理的批准,但他并不知道具体是哪个经理会批准。

观察者模式在Java和常见的框架中十分常见

在Java和Spring中,观察者模式被广泛应用。以下是几个常见的例子:

  1. Java内置的事件处理机制:Java提供了一套事件处理机制,其中就包括观察者模式的实现。例如,当用户点击一个按钮时,该按钮会生成一个事件(如ActionEvent),并通知所有注册的事件监听器(如ActionListener)执行相应的回调方法;

  2. Swing GUI编程框架:Swing是Java提供的GUI编程框架,它使用观察者模式来处理用户界面和程序之间的交互。例如,当用户输入数据时,文本框会生成一个事件(如TextEvent),并通知所有注册的事件监听器(如TextListener)执行相应的回调方法;

  3. Spring框架中的事件机制:Spring框架也提供了一套事件机制,其中就包括观察者模式的实现。例如,当某个业务逻辑发生变化时,可以通过发布相关的事件(如ApplicationEvent),并通知所有注册的事件监听器(如ApplicationListener)执行相应的回调方法。

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

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

相关文章

【C++】二叉搜索树的应用

前言 二叉搜索树本质也是二叉树&#xff0c;但因为其数据存储的特殊 — 左子树的值都更小&#xff0c;右子树的值都更大&#xff0c;所以在大部分情况下&#xff0c;查找更为高效。本篇博客将讲述二叉搜索树两个应用搜索的场景 那么话不多说&#xff0c;马上开始今天的学习。 文…

linux运维必了解的日志文件系统

目录 一、inode与block1.1inode和block概述1.1.1inode和block的关系 1.2inode的内容1.2.1inode包含文件的元信息1.2.2linux文件系统的三个时间戳1.2.3目录文件结构 1.3inode的号码1.3.1 硬盘分区后的结构 1.4inode的大小1.5恢复误删除的xfs文件1.6EXT类型文件恢复误删除 二、分…

亿发软件:玩具批发行业需要怎样的进销存开单软件

中国玩具市场的发展潜力十分巨大&#xff0c;近五年来中国玩具行业的批发零售企业都保持着良好的发展态势。近年来&#xff0c;在数字化转型的时代浪潮下&#xff0c;玩具批发零售市场想实现进一步的高质量发展&#xff0c;充分满足客户多元化的供货需求&#xff0c;需要向数据…

Qt5 和 OpenCV4 计算机视觉项目:6~9

原文&#xff1a;Qt 5 and OpenCV 4 Computer Vision Projects 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 当别人说你没有底线的时候&#x…

OpenCV 图像处理学习手册:6~7

原文&#xff1a;Learning Image Processing with OpenCV 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 当别人说你没有底线的时候&#xff0c;…

javascript之数组

创建 有以下几种方式 1.构造函数 在使用构造函数时&#xff0c;可以不带new 创建空数组 let color new Array() 或者let color Array() 创建指定个数元素的数组 let color new Array(2) 或者let color Array(2) 创建指定元素的数组 let color new Array("bl…

atio函数和宏offset的介绍

目录 前言atoi函数宏offsetof总结 前言 本章带大家一起认识一些在我们C语言标准库中的函数 atoi函数 int atio(const char* str);头文件&#xff1a; #include<stdlib.h>参数&#xff1a; str指向常量字符串起始位置的指针 函数介绍&#xff1a; ①解析C语言字符串str,…

2023软件测试工程师必备技能?要卷,谁还不会了......

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 软件测试岗位是怎…

FPGA基于Tri Mode Ethernet MAC实现UDP通信 提供3套工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、米联客UDP协议栈4、详细设计方案5、Tri Mode Ethernet MAC的使用6、vivado工程1详解7、vivado工程2详解8、vivado工程3详解9、上板调试验证并演示10、福利&#xff1a;工程代码的获取 1、前言 目前网上的fpga实现udp基本生态如下&#x…

Spring Security实战(五)—— 密码加密

一、密码加密的演进 MD5 (Message-Digest Algorithm 5) 和 SHA (Secure Hash Algorithm) 是两种常见的消息摘要算法&#xff0c;它们都被用于加密和数据完整性验证等领域。 MD5 是一种 128 位的哈希函数&#xff0c;常用于数据完整性校验和数字签名等方面。它将任意长度的信息…

WebServer项目(四)->(基于Proactor的c++)Web服务器简介及简单实现

基于Proactor的cWeb服务器项目 WebServer项目(四)-&#xff1e;(基于Proactor的c)Web服务器简介及简单实现1.Web Server&#xff08;网页服务器&#xff09;2.HTTP协议(应用层的协议)3.HTTP 请求报文格式4.HTTP响应报文格式5.HTTP请求方法6.HTTP状态码7.服务器编程基本框架8.两…

分布式搜索技术elasticsearch概念篇

文章目录 一、分布式搜索技术二、elasticsearch2.1 初识elasticsearch2.2 正向索引和倒排索引2.2.1 介绍2.2.2 优缺点 2.3 elasticsearch和mysql的对比 一、分布式搜索技术 分布式搜索技术是一种基于分布式计算的搜索引擎技术&#xff0c;它使用多台计算机协同工作来处理大规模…

ElementUI登陆表单中常用的标签属性

ElementUI官网 为登陆框添加一个边角弧度 <style> .className{/*设置div边边框角的弧度*/border-radius: 10px; } </style><el–input>标签常用属性 <!--使用prefix属性添加一个前缀图标--> <el-input prefix-icon"el-icon-user-solid"&g…

深度学习第J6周:ResNeXt-50实战解析

目录 一、模型结构介绍 二、前期准备 三、模型 三、训练运行 3.1训练 3.2指定图片进行预测 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] &#x1f4cc; …

大数据技术之集群数据迁移

在大数据集群数据迁移的项目中涉及到很多技术细节&#xff0c;本博客记录了迁移的大致的操作步骤。 迁移借用Hadoop自带的插件&#xff1a;distcp。 一、Hadoop集群数据迁移 **DistCp&#xff08;分布式拷贝&#xff09;**是用于大规模集群内部和集群之间拷贝的工具。它使用M…

Meson构建系统的使用

一、前言 Meson 是用于自动化构建的自由软件&#xff0c;使用Python语言编写&#xff0c;在 Apache 许可证 2.0版本下发布&#xff0c;主要目标是为了让开发者节约用于配置构建系统的时间。 特点如下&#xff1a; 多平台支持&#xff0c;包括 GNU/Linux、Windows、MacOS、GCC、…

A_A01_008 STM32F103系列标准库移植经验分享

A_A01_008 STM32F103系列标准库移植经验分享 一、所需材料二、移植步骤三、注意事项四、参考资料与友情链接 一、所需材料 1.MDK开发环境 此处版本V5.15 2.相关启动文件 此处用野火点灯例程 因为启动文件完整 方便更换 其它工程没有的可以直接复制这些启动文件过去 3.相关开…

Java版工程管理系统源代码-软件自主研发,工程行业适用

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示…

8年测开年薪30W,为什么从开发转型为测试?谈谈这些年的心路历程……

谈谈我的以前&#xff0c;从毕业以来从事过两个多月的Oracle开发后转型为软件测试&#xff0c;到现在已近过去8年成长为一个测试开发工程师&#xff0c;总结一下之间的心路历程&#xff0c;希望能给徘徊在开发和测试之前的同学一点小小参考。 一、测试之路伏笔 上学偷懒&#…

EGO-Link FPGA智慧教育社区介绍:用 leetcode 的方式练习 verilog 语言

文章目录 介绍FPGA 语法例1&#xff1a;P1203 1输入1输出例2&#xff1a;P1204 3输入4输出例3&#xff1a;P1207 P1208 P1205 与或非门例4&#xff1a;P1200 半加器例5&#xff1a;P1201 4位二进制转余3循环码例6&#xff1a;P1215 2选1多路选择器例7&#xff1a;P1236 D触发器…