【重温设计模式】观察者模式及其Java示例

观察者模式的概念和原理

在编程世界中,设计模式作为一种解决问题的策略,它的存在就如同人类语言中的成语,是一种经过时间考验的有效解决方案。

观察者模式就是其中一种重要的设计模式,它在很多场景中都有着广泛的应用。那么,什么是观察者模式呢?观察者模式,又被称为发布-订阅模式,其基本概念是定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

它的设计原理就像是一场戏,主角(主题)在舞台上表演,观众(观察者)在台下观看。当主角的表演发生变化时,观众能立即感知到这种变化。在编程领域,观察者模式的应用场景十分广泛,比如GUI中的事件处理,社交网络中的消息推送,甚至是股票交易系统中的价格更新等等。

class OneMoreSubject {
    private List<Observer> observers = new ArrayList<>();

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

    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

以上是一个简单的观察者模式的Java代码示例,OneMoreSubject类就是我们的主题,它维护了一个观察者列表。当有新的观察者加入时,我们通过addObserver方法将其加入到观察者列表中。当主题的状态发生变化时,我们通过notifyAllObservers方法通知所有的观察者。这样,我们就实现了观察者模式的基本框架。接下来,我们将详细解释观察者模式的主要组成部分,包括主题(Subject)和观察者(Observer),并解释它们的角色和互动方式。

观察者模式的组成部分

在探讨观察者模式的具体应用之前,我们首先需要理解它的主要组成部分。观察者模式主要由两个角色构成,它们分别是主题(Subject)和观察者(Observer)。主题可以被视为数据的提供者,它持有一些重要的状态信息。当这些状态发生变化时,主题会通知所有的观察者。观察者则是数据的接收者,它们订阅了主题,并在主题状态变化时接收通知。主题与观察者之间的关系,就像是一份报纸与它的订阅者,或者是一个B站UP主与它的关注者。

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

public interface Observer {
    void update(OneMore oneMore);
}

在这个简单的Java代码示例中,Subject接口声明了注册(register)、移除(remove)和通知(notify)观察者的方法,而Observer接口则声明了一个更新(update)方法,用于接收主题的状态变化通知。这就是观察者模式的基本构成,通过这种方式,我们可以将数据的提供者和接收者解耦,使得在数据状态变化时,可以灵活地通知到所有关注此变化的观察者。

有了对观察者模式组成部分的理解,我们就可以进一步探讨如何在实际项目中应用观察者模式了。在下一部分,我们将通过一个电子商务系统的Java实例,展示观察者模式的工作流程。

观察者模式的Java实例

在我们刚刚探讨过的观察者模式的主要组成部分——主题(Subject)和观察者(Observer)之后,现在让我们通过一个具体的Java实例来看看观察者模式是如何在实际项目中发挥作用的。假设我们正在开发一个电子商务系统,当一个商品的库存数量发生变化时,需要通知相关的销售人员和采购人员,这就是一个典型的观察者模式的应用场景。

首先,我们定义一个Subject类,这个类有一个OneMore的商品库存数量,当这个数量发生变化时,就需要通知所有的观察者。

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

    public void setOneMore(int OneMore) {
        this.OneMore = OneMore;
        notifyAllObservers();
    }

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

    public void notifyAllObservers(){
        for (Observer observer : observers) {
            observer.update();
        }
    } 
}

然后,我们定义一个Observer接口和两个实现这个接口的类:SalesObserverPurchaseObserver,分别代表销售人员和采购人员。

public interface Observer {
    void update();
}

public class SalesObserver implements Observer {
    public void update() {
        System.out.println("SalesObserver: OneMore's stock has changed.");
    }
}

public class PurchaseObserver implements Observer {
    public void update() {
        System.out.println("PurchaseObserver: OneMore's stock has changed.");
    }
}

OneMore的库存数量发生变化时,Subject类就会调用notifyAllObservers方法,通知所有的观察者。这样,无论是销售人员还是采购人员,都能够得到库存变化的通知,从而做出相应的行动。这就是观察者模式的工作流程。

implements
implements
contains
1
*
Subject
-int OneMore
-List<Observer> observers
+setOneMore(int OneMore)
+attach(Observer observer)
+notifyAllObservers()
«interface»
Observer
+update()
SalesObserver
+update()
PurchaseObserver
+update()

然而,任何设计模式都有其适用的场景,也有其优缺点。接下来,我们将探讨观察者模式的优缺点,以及在何种情况下应该使用观察者模式。

观察者模式的优缺点

在我们深入探讨观察者模式的优缺点之前,让我们先来描绘一下这种模式的特色。观察者模式是一种行为设计模式,它允许对象在状态发生变化时通知其依赖对象,而无需它们之间直接耦合。这种模式的主要优点是它支持单向依赖,这意味着主题并不关心观察者的具体实现,只关心观察者是否遵循了预定义的接口。这种设计大大提高了代码的灵活性和可重用性。

然而,这种模式也有其缺点。首先,观察者模式可能导致过度通信。当主题的状态发生改变时,所有的观察者都会被通知,即使他们对该变化没有任何兴趣。其次,如果观察者和主题之间存在循环依赖,可能会导致系统陷入无限循环。最后,如果观察者的处理时间过长,可能会阻塞主题的进程。

对于何时使用观察者模式,一般的建议是当你希望在对象的状态改变时,能够通知其他对象,但又不希望这些对象之间存在直接依赖关系时,可以考虑使用观察者模式。例如,一个邮件订阅系统就可以使用观察者模式,当新的邮件到达时,所有订阅者都会收到通知。

总结

总的来说,观察者模式是一种强大的设计模式,它能够帮助我们在系统中实现松耦合的对象间通信。通过这种模式,我们可以使得一个对象的状态变化能够通知到其他的对象,而不需要这些对象之间存在直接的联系。这样,我们就可以实现对象间的动态交互,使得系统更加灵活和可扩展。

但是,我们也需要注意,观察者模式并不是万能的。在某些情况下,它可能会导致过度通信,或者造成系统的循环依赖。因此,我们在使用观察者模式时,需要仔细考虑其适用性,确保它能够帮助我们解决问题,而不是引入新的问题。

我们可能会遇到许多需要使用观察者模式的场景。比如,我们可能需要实现一个社交网络的消息推送系统,或者一个电子商务网站的库存管理系统。在这些场景中,观察者模式都能够发挥其强大的作用。

然而,设计模式只是工具,而不是目标。我们的最终目标,是通过使用这些工具,来编写出高效、可维护、可扩展的代码。因此,我们应该根据实际的需求和场景,灵活地选择和使用设计模式,而不是死板地套用。

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

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

相关文章

【开发】Redis 的理解与数据存储格式

目录 相关传送门 1. NOSQL和关系型数据库比较 2. 主流的NOSQL产品 3. Redis的理解 4. redis数据存储格式 4.1 String 4.2 Hash 4.3 List 4.4 Set 4.5. sorted_set 注&#xff1a;手机端浏览本文章可能会出现 “目录”无法有效展示的情况&#xff0c;请谅解&#xf…

旅游行业分析及媒体邀约资源汇总

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 酒店旅游行业分析及媒体邀约资源汇总是两个相对独立但又相互关联的领域。下面将分别对这两个方面进行概述。 酒店旅游行业分析 1. 市场概况 市场规模&#xff1a;评估市场的总价值、增长…

云原生(四)、Docker-Compose

Docker-Compose Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它使用一个简单的 YAML 文件来配置应用程序的服务、网络和卷&#xff0c;从而使得在不同环境中轻松部署应用程序变得更加简单和可靠。 Docker Compose 主要由以下几个核心组件组成&#xf…

【SQL】1341. 电影评分(分组求解+合并union all;order by 多字段排序)

前述 知识点回顾&#xff1a;union all和union的区别 Union&#xff1a;对两个结果集进行并集操作&#xff0c;不包括重复行&#xff0c;同时进行默认规则的排序&#xff1b;Union All&#xff1a;对两个结果集进行并集操作&#xff0c;包括重复行&#xff0c;不进行排序&…

主机与windows虚拟机远程桌面实现方法

目录 一、虚拟机相关配置1. 配置虚拟机网络2. 打开虚拟机远程桌面功能3. 配置虚拟机用户与分组 二、主机相关配置 当无法通过共享文件夹实现主机与windows虚拟机文件共享时&#xff0c;可以通过主机与虚拟机远程桌面的方法实现文件的共享传输。本文主要介绍主机与虚拟机远程桌面…

Django 应用的路由访问

项目url 添加应用访问路径 from django.contrib import admin from django.urls import path, include from app1 import viewsurlpatterns [path(admin/, admin.site.urls),path(app1/, include(app1.urls)), # 在主项目添加应用的所有路由路径 ] 就可以访问app1应用下的ur…

【Python】第十二章_外星人入侵_武装飞船

目录 项目概述&#xff1a; 1 项目需求分析 2 安装Pygame 3 开始游戏项目 3.1 创建Pygame窗口以及响应用户输入 3.2 设置背景色 3.3 创建设置类 4 添加飞船图像 4.1 创建Ship 类 4.2 在屏幕上绘制飞船 5 重构&#xff1a; 模块game_functions 5.1 函数check_even…

osgEarth学习笔记2-第一个Osg QT程序

原文链接 上个帖子介绍了osgEarth开发环境的安装。本帖介绍我的第一个Osg QT程序。 下载 https://github.com/openscenegraph/osgQt 解压&#xff0c;建立build目录。 使用Cmake-GUI Configure 根据需要选择win32或者x64&#xff0c;这里我使用win32. 可以看到include和lib路…

注册个人小程序

访问地址 https://mp.weixin.qq.com/ 立即注册 选择小程序 注册 填写信息 登录邮箱 访问邮箱的链接激活账号 选择个人&#xff0c;填写信息 注册完成&#xff0c;即可登录进入填写信息

数通-OSPF域间路由防环机制

1类LSA&#xff1a;用来描述路由器自身直连接口链路状态信息的&#xff08;也就是路由器连了什么&#xff09;&#xff1b; 2类LSA&#xff1a;用来描述伪节点的信息&#xff08;2类LSA不仅描述了拓扑信息&#xff0c;同时也描述了叶子信息&#xff09;&#xff1b; 3类LSA&a…

后端工程师快速使用axios

文章目录 01.AJAX 概念和 axios 使用模板目标讲解代码解析案例前端后端结果截图 02.URL 查询参数模板目标讲解案例前端后端结果截图 03.常用请求方法和数据提交模板目标讲解案例前端后端结果截图 04.axios 错误处理模板目标讲解案例前端后端结果截图 01.AJAX 概念和 axios 使用…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之一 哈哈镜效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之一 哈哈镜效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之一 哈哈镜效果 一、简单介绍 二、简单哈哈镜实现的原理 1、图像拉伸放大 2、图像缩小 三、哈哈镜 拉伸放大 代码实现 …

uniapp可视范围高度 - 用户屏幕可操作的屏幕高度 - 适用于APP、H5@公众号、纯H5@Chrome

可视范围高度 let heightPx uni.getWindowInfo().windowHeight uni.getWindowInfo().windowTop 官方手册 uni.getWindowInfo() | uni-app官网uni-app,uniCloud,serverless,uni.getWindowInfo()https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html 实测数据 uni.ge…

【目标检测】2. RCNN

接上篇 【目标检测】1. 目标检测概述_目标检测包括预测目标的位置吗?-CSDN博客 一、前言 CVPR201 4经典paper:《 Rich feature hierarchies for accurate object detection and semantic segmentation》&#xff0c;https://arxiv.org/abs/1311.2524, 这篇论文的算法思想被称…

信息检索(十三):On Complementarity Objectives for Hybrid Retrieval

On Complementarity Objectives for Hybrid Retrieval 摘要1. 引言2. 相关工作2.1 稀疏和密集检索2.2 互补性 3. 提出方法3.1 Ratio of Complementarity (RoC)3.2 词汇表示&#xff08;S&#xff09;3.3 语义表示&#xff08;D&#xff09;3.4 互补目标 4. 实验4.1 实验设置4.2…

服务器病毒木马通用排查处理应急响应流程

目录 一、勒索病毒发作的特征 二、勒索病毒的应急响应 三、勒索病毒预防与事后加固 一、勒索病毒发作的特征 如果发现大量统一后缀的文件&#xff1b;发现勒索信在Linux/home、/usr等目录&#xff0c;在Windows 桌面或者是被加密文件的文件夹下。如果存在以上特…

Flutter-仿腾讯视频Banner效果

闲聊 人一旦运气差&#xff0c;喝水都能噎着。我又被发”毕业证“了&#xff0c;&#x1f62d;&#xff0c;对&#xff01;&#xff01;&#xff01;没有听错&#xff0c;发毕业证的当天上午刚讨论完需求&#xff0c;中午吃完饭&#xff0c;正常去公司前面的小公园溜达&#x…

供应链投毒预警 | 开源供应链投毒202402月报发布啦

概述 悬镜供应链安全情报中心通过持续监测全网主流开源软件仓库&#xff0c;结合程序动静态分析方式对潜在风险的开源组件包进行动态跟踪和捕获&#xff0c;发现大量的开源组件恶意包投毒攻击事件。在2024年2月份&#xff0c;悬镜供应链安全情报中心在NPM官方仓库&#xff08;…

openEuler-22.03-LTS-SP2更改阿里云yum安装源

备份文件/etc/yum.repos.d/openEuler.repo&#xff0c;并将文件替换为以下内容&#xff1a; [OS] nameOS baseurlhttps://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/OS/$basearch/ enabled1 gpgcheck1 gpgkeyhttps://mirrors.aliyun.com/openeuler/openEuler-22.…

Tomcat(二)

一、搭建个人博客 二、状态页 默认的管理页面被禁用&#xff0c;启用方法如下 修改conf/conf/tomcat-users.xml 2.1 开启状态页&#xff08;本地访问&#xff09; 2.2 开启允许远程登录状态页 2.3 host manager