【设计模式】观察者模式 (发布-订阅模式,模型-视图模式,源-监听器模式,从属者模式)

观察者模式(Observer Pattern)详解


一、观察者模式简介

观察者模式(Observer Pattern) 是一种 行为型设计模式(对象行为型模式),它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。

你可以把它想象成“订阅-发布”机制:当你订阅了一份杂志后,每当有新一期出版时,你就会收到通知。这里的“你”就是观察者,“杂志”则是被观察的主题。

在这里插入图片描述
交通信号灯 <- -> 观察目标
汽车(汽车驾驶员) <- -> 观察者
(一对多)

软件系统:一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动。

观察者模式

  1. 定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象
  2. 发生改变的对象称为观察目标,被通知的对象称为观察者
  3. 一个观察目标可以对应多个观察者

别名
发布-订阅(Publish/Subscribe)模式
模型-视图(Model/View)模式
源-监听器(Source/Listener)模式
从属者(Dependents)模式

观察者模式包含以下4个角色
Subject(目标)
ConcreteSubject(具体目标)
Observer(观察者)
ConcreteObserver(具体观察者)


二、解决的问题类型

观察者模式主要用于解决以下问题:

  • 状态变化需要通知相关联的对象:例如,用户界面中的模型数据发生变化时,视图需要自动更新。需要在系统中创建一个触发链。
  • 避免紧耦合:允许对象之间保持松散的联系,无需直接相互引用。一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 实现广播通信:可以向所有注册的观察者发送消息,而不需要知道它们的具体身份。

三、使用场景

场景示例
GUI事件处理如按钮点击触发窗口刷新
消息传递系统如发布/订阅模式的消息队列
数据模型与视图同步MVC架构中,Model数据变化时通知View更新
实时数据监控如股票价格变动时通知所有关注的客户端

四、核心概念

  1. Subject(主题/被观察者):维护了一个观察者列表,并提供方法供观察者注册或移除自身;在状态改变时通知所有观察者。
  2. Observer(观察者):接收来自主题的通知并作出响应。
  3. ConcreteSubject & ConcreteObserver:具体实现上述两个接口的实际类。

五、实际代码案例(Java)

1. 定义 Observer 接口

// 观察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}

2. 定义 Subject 接口

import java.util.ArrayList;// 主题接口
public interface Subject {void registerObserver(Observer o); // 注册观察者void removeObserver(Observer o);   // 移除观察者void notifyObservers();            // 通知所有观察者
}

3. 创建具体的 WeatherData 类作为被观察者

public class WeatherData implements Subject {private ArrayList<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void measurementsChanged() {notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}

4. 创建具体的观察者类

public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}

5. 测试代码

public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}

输出结果:

Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Current conditions: 78.0F degrees and 90.0% humidity

典型代码

典型的抽象目标类代码

using System.Collection
abstract class Subject
{//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers = new ArrayList();
//声明抽象注册方法,用于向观察者集合中增加一个观察者public abstract void Attach(Observer observer);
//声明抽象注销方法,用于在观察者集合中删除一个观察者public abstract void Detach(Observer observer);//声明抽象通知方法public abstract void Notify();
}

典型的具体目标类代码

class ConcreteSubject : Subject
{public override void Attach(Observer observer){observers.Add(observer);}public override void Detach(Observer observer){observers.Remove(observer);}//实现通知方法public override void Notify(){//遍历观察者集合,调用每一个观察者的响应方法foreach(object obs in observers){((Observer)obs).Update();}}
}

典型的抽象观察者代码

interface Observer
{void Update();
}

典型的具体观察者代码

class ConcreteObserver : Observer
{//实现响应方法public void Update(){//具体更新代码}
}

典型的客户端代码片段

……
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.Attach(observer);
subject.Notify();
……

其他案例

  1. 在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。
    现使用观察者模式设计并实现该过程,以实现战队成员之间的联动。
    战队成员之间的联动过程:
    联盟成员受到攻击 --> 发送通知给盟友 --> 盟友做出响应
    在这里插入图片描述

在这里插入图片描述

  1. 猫、狗与老鼠
    假设猫是老鼠和狗的观察目标,老鼠和狗是观察者,猫叫老鼠跑,狗也跟着叫,使用观察者模式描述该过程。

在这里插入图片描述


六、优缺点分析

优点描述
解耦主题和观察者之间是松散耦合的,便于独立开发和修改。可以实现表示层和数据逻辑层的分离。
支持广播通信可以轻松地将信息广播给多个观察者
易于扩展新增观察者只需实现 Observer 接口即可,不影响现有代码。符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便
缺点描述
可能导致性能问题如果观察者数量过多,通知过程可能会变得缓慢
潜在的内存泄漏风险若未正确移除观察者,可能导致无法释放资源。如果存在循环依赖时可能导致系统崩溃
难以追踪依赖关系复杂的观察者网络可能使程序难以调试和理解
其他没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化

七、与其他模式对比(补充)

模式名称目标
命令模式封装请求为对象,支持请求排队、日志记录等操作
中介者模式减少对象之间的直接交互,通过中介者进行协调
观察者模式维持对象间的一对多依赖关系,实现状态变更的通知

八、最终小结

观察者模式是一种非常实用的设计模式,特别适合那些需要在不同组件之间维持松散耦合并且能够响应状态变化的应用场景。通过合理地运用观察者模式,我们可以在不破坏原有模块独立性的前提下,有效地实现组件间的协作与通信。

在开发图形用户界面(GUI)、实时数据监控系统、消息中间件等项目时,观察者模式能够帮助你更好地组织代码结构,提高系统的灵活性和可维护性。


📌 一句话总结:

观察者模式就像一个新闻通讯社,一旦有新的新闻发布,所有订阅了该新闻的人都会立即收到通知。

部分内容由AI大模型生成,注意识别!

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

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

相关文章

Linux的`<< EOF`(Here-Document)详解多回答笔记250722

Linux的<< EOF(Here-Document)详解多回答笔记250722 Linux 中的 << EOF 结构称为 Here Document&#xff08;立即文档或嵌入文档&#xff09;&#xff0c;它是一种在 Shell 脚本中直接嵌入多行文本输入流&#xff08;通常作为命令的标准输入&#xff09;的方式。E…

Go语言实战案例-简单配置文件(INI格式)解析器

以下是《Go语言100个实战案例》中的 文件与IO操作篇 - 案例20&#xff1a;简单配置文件&#xff08;INI格式&#xff09;解析器 的完整内容&#xff0c;适合入门学习如何用 Go 语言解析常见的 .ini 配置文件格式。&#x1f3af; 案例目标使用 Go 语言解析一个 .ini 格式的配置文…

用 PyTorch 实现全连接网络识别 MNIST 手写数字

目录 一、什么是全连接网络 二、代码实现步骤 1. 导入必要的库 2. 数据准备 3. 定义网络结构 4. 模型训练 5. 模型保存和加载 6. 预测单张图片 7. 主函数 三、运行结果说明 四、小结 一、什么是全连接网络 全连接神经网络&#xff08;Fully Connected Neural Networ…

vscode怎么安装MINGW

下载&#xff1a; 第一步选择MINGW官网&#xff1a;MinGW-w64 - for 32 and 64 bit Windows - SourceForge.net 点击Files 点击Toolchains targetting Win64 点击第一个 Personal Builds 点击mingw-builds 选择8.1.0 点击第二个 threads-posix 点击第二个seh 最后左键点击下…

CSS图片分层设置

在CSS中实现图片分层效果&#xff0c;主要通过定位属性和层叠上下文控制。以下是核心实现方法和示例&#xff1a; 一、核心实现原理定位方式 使用 position: relative/absolute/fixed 使图片脱离文档流 .layer {position: absolute; /* 关键属性 */top: 0;left: 0; }层叠控制 通…

GEMINUS 和 Move to Understand a 3D Scene

论文链接&#xff1a;https://arxiv.org/abs/2507.14456 代码链接&#xff1a;https://github.com/newbrains1/GEMINUS 端到端自动驾驶的挑战 端到端自动驾驶是一种“一站式”方法&#xff1a;模型直接从传感器输入&#xff08;如摄像头图像&#xff09;生成驾驶轨迹或控制信号…

算法与数据结构:线性表

C语言数据结构基础&#xff1a;线性表详解线性表是数据结构中最基础、最常用的形式&#xff0c;就像一列整齐排队的游客&#xff1a;每个元素有固定位置&#xff08;前驱和后继&#xff09;&#xff0c;长度可动态变化。在C语言中&#xff0c;它主要通过顺序表&#xff08;数组…

制作mac 系统U盘

使用 installinstallmacos.py&#xff08;更兼容&#xff09; 苹果官方不提供所有历史版本的安装器&#xff0c;但可以通过一个开源脚本下载&#xff08;Apple 提供的企业支持工具&#xff09;&#xff1a; git clone https://github.com/munki/macadmin-scripts.git cd macadm…

渗透部分总结

docker环境搭建以及dns等原理讲解Docker搭建&#xff1a;Linux 系统上安装 Docker 引擎并启动服务&#xff1a;# 安装Docker引擎 curl -fsSL https://get.docker.com | sh 通过 curl 下载并执行 Docker 官方的安装脚本&#xff0c;这会自动配置 Docker 仓库并安装最新版本的 Do…

k8s pvc是否可绑定在多个pod上

1.pvc是否可绑定在多个podPVC 是否能被多个 Pod 使用&#xff0c;取决于它的 accessModes。PVC 的 accessModes是否支持多个 Pod 同时使用说明ReadWriteOnce (RWO)❌ 若多个Pod&#xff0c;需在相同节点上&#xff08;仅允许被单个节点上的Pod挂载&#xff09;常用于本地磁盘、…

如何加固Endpoint Central服务器的安全?(下)

Endpoint Central 作为企业终端管理的 “中枢系统”&#xff0c;掌控着全网终端的补丁推送、软件部署、配置管理、远程控制等关键权限&#xff0c;存储着大量终端资产信息、用户数据及企业策略配置。一旦服务器被攻破&#xff0c;攻击者可能篡改管理指令&#xff08;如推送恶意…

信息整合注意力IIA,通过双方向注意力机制重构空间位置信息,动态增强目标关键特征并抑制噪声

在遥感图像语义分割等视觉任务中&#xff0c;编码器 - 解码器结构通过跳跃连接融合多尺度特征时&#xff0c;常面临两大挑战&#xff1a;一是编码器的局部细节特征与解码器的全局语义特征融合时&#xff0c;空间位置信息易丢失&#xff0c;导致目标定位不准&#xff1b;二是复杂…

如何迁移jenkins至另一台服务器

前言公司旧的服务器快到期了&#xff0c;需要将部署在其上的jenkins整体迁移到另一台服务器&#xff0c;两台都是aws ec2服务器。文章主要提供给大家一种迁移思路&#xff0c;并不一定是最优解&#xff0c;仅供参考&#xff0c;大家根据实际情况自行选用和修改&#xff0c;举一…

在vue中遇到Uncaught TypeError: Assignment to constant variable(常亮无法修改)

1.问题如下:2.出现这个问题的原因----在设计变量的时候采用了const来进行修饰,在修改的时候直接对其进行修改3.利用响应式变量的特点&#xff0c;修改为下面这样就可以正常了

RCE随笔-奇技淫巧(2)

Linux命令长度限制在7个字符的情况下&#xff0c;如何拿到shell <?php $param $_REQUEST[param]; If ( strlen($param) < 8 ) { echo shell_exec($param); }分析代码&#xff1a;这段代码传入参数param然后进入if语句判断是否小于8个字符&#xff0c;然后如果小于就会进…

设计模式九:构建器模式 (Builder Pattern)

动机(Motivation)1、在软件系统中&#xff0c;有时候面临着“一个复杂对象”的创建工作&#xff0c;其通常由各个部分的子对象用一定的算法构成&#xff1b;由于需求的变化&#xff0c;这个复杂对象的各个部分经常面临着剧烈的变化&#xff0c;但是将它们组合在一起的算法却相对…

如何高效合并音视频文件

在自我学习或者进行视频剪辑的时候&#xff0c;经常从资源网址下载音视频分离的文件&#xff0c;例如audio_file1.m4a和video_1.mp4&#xff0c;之后需要把这两个文件合并在一起。于是条件反射得想要利用剪映等第三方工具&#xff0c;进行音视频的封装。可惜不幸的是&#xff0…

虚幻 5 与 3D 软件的协作:实时渲染,所见所得

《曼达洛人》的星际飞船在片场实时掠过虚拟荒漠&#xff0c;游戏开发者拖动滑块就能即时看到角色皮肤的通透变化&#xff0c;实时渲染技术正以 “所见即所得” 的核心优势&#xff0c;重塑着 3D 创作的整个逻辑。虚幻引擎 5&#xff08;UE5&#xff09;凭借 Lumen 全局光照和 N…

​Eyeriss 架构中的访存行为解析(腾讯元宝)

​Eyeriss 架构中的访存行为解析​Eyeriss 是 MIT 提出的面向卷积神经网络&#xff08;CNN&#xff09;的能效型 NPU&#xff08;神经网络处理器&#xff09;架构&#xff0c;其核心创新在于通过硬件结构优化访存行为&#xff0c;以解决传统 GPU 在处理 CNN 时因数据搬运导致的…

数字图像处理(三:图像如果当作矩阵,那加减乘除处理了矩阵,那图像咋变):从LED冬奥会、奥运会及春晚等等大屏,到手机小屏,快来挖一挖里面都有什么

数字图像处理&#xff08;三&#xff09;一、&#xff08;准备工作&#xff1a;咋玩&#xff0c;用什么玩具&#xff09;图像以矩阵形式存储&#xff0c;那矩阵一变、图像立刻跟着变&#xff1f;1. Python Jupyter Notebook/Lab 库 (NumPy, OpenCV, Matplotlib, scikit-image…