当前位置: 首页 > news >正文

设计模式】Listener模式和Visitor模式的区别

文章目录

  • 前言
  • 一、介绍
      • Listener模式
      • Visitor模式
  • 二、代码实现
      • 2.1 Listener模式的Java实现
      • 2.2Listener模式的Go实现
      • 2.3Visitor模式的Java实现
      • 2.4Visitor模式的Go实现
  • 三、总结


前言

在软件设计中,设计模式是解决特定问题的通用解决方案。Listener模式和Visitor模式是两种常见的行为设计模式,它们在不同的场景下提供了解决问题的有效方法。本文将详细解释这两种模式,并通过Java和Go语言的代码示例展示它们的实现,最后总结它们的区别和适用场景。


一、介绍

Listener模式

Listener模式(监听器模式) 是一种行为设计模式,主要用于事件驱动的编程。它允许一个对象(监听器)注册到另一个对象(事件源),以便在特定事件发生时接收通知。

主要特点:
1.解耦: 事件源和监听器之间是松耦合的,事件源只需要知道监听器实现了某个接口,而不需要知道具体的实现细节。
2.灵活性: 可以动态添加或移除监听器。
3.异步处理: 事件通知通常是异步的,这意味着事件源在触发事件后可以继续执行其他任务,而不需要等待监听器处理完事件。

典型应用:

  • GUI应用程序中的按钮点击事件。
  • 网络编程中的数据接收事件。

Visitor模式

Visitor模式(访问者模式) 是一种行为设计模式,它允许你在不改变对象结构的前提下,定义作用于这些对象的新操作。它将操作的定义与对象结构分离,使得新的操作可以很容易地添加。

主要特点:
1.双重分派: Visitor模式使用双重分派机制,即首先调用对象的accept方法,然后在accept方法中调用访问者的visit方法。
2.扩展性: 可以很容易地添加新的操作,而不需要修改对象结构。
**3.复杂性:**增加了系统的复杂性,因为需要定义多个访问者类和accept方法。

典型应用:

  • 编译器中的语法树遍历。
  • 复杂对象结构的操作,如文件系统遍历。

二、代码实现

2.1 Listener模式的Java实现

// 定义监听器接口
public interface EventListener {void onEvent();
}// 事件源类
public class EventSource {private List<EventListener> listeners = new ArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}public void removeListener(EventListener listener) {listeners.remove(listener);}public void triggerEvent() {for (EventListener listener : listeners) {listener.onEvent();}}
}// 实现监听器
public class MyEventListener implements EventListener {@Overridepublic void onEvent() {System.out.println("Event triggered!");}
}// 使用示例
public class Main {public static void main(String[] args) {EventSource eventSource = new EventSource();MyEventListener listener = new MyEventListener();eventSource.addListener(listener);eventSource.triggerEvent();}
}

2.2Listener模式的Go实现

package mainimport ("fmt"
)// 定义监听器接口
type EventListener interface {OnEvent()
}// 事件源类
type EventSource struct {listeners []EventListener
}func (es *EventSource) AddListener(listener EventListener) {es.listeners = append(es.listeners, listener)
}func (es *EventSource) RemoveListener(listener EventListener) {for i, l := range es.listeners {if l == listener {es.listeners = append(es.listeners[:i], es.listeners[i+1:]...)break}}
}func (es *EventSource) TriggerEvent() {for _, listener := range es.listeners {listener.OnEvent()}
}// 实现监听器
type MyEventListener struct{}func (mel *MyEventListener) OnEvent() {fmt.Println("Event triggered!")
}func main() {eventSource := &EventSource{}listener := &MyEventListener{}eventSource.AddListener(listener)eventSource.TriggerEvent()
}

2.3Visitor模式的Java实现

// 定义访问者接口
public interface Visitor {void visit(ElementA element);void visit(ElementB element);
}// 定义元素接口
public interface Element {void accept(Visitor visitor);
}// 具体元素A
public class ElementA implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 具体元素B
public class ElementB implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 具体访问者
public class ConcreteVisitor implements Visitor {@Overridepublic void visit(ElementA element) {System.out.println("Visiting ElementA");}@Overridepublic void visit(ElementB element) {System.out.println("Visiting ElementB");}
}// 使用示例
public class Main {public static void main(String[] args) {List<Element> elements = Arrays.asList(new ElementA(), new ElementB());Visitor visitor = new ConcreteVisitor();for (Element element : elements) {element.accept(visitor);}}
}

2.4Visitor模式的Go实现

package mainimport ("fmt"
)// 定义访问者接口
type Visitor interface {VisitElementA(*ElementA)VisitElementB(*ElementB)
}// 定义元素接口
type Element interface {Accept(Visitor)
}// 具体元素A
type ElementA struct{}func (e *ElementA) Accept(visitor Visitor) {visitor.VisitElementA(e)
}// 具体元素B
type ElementB struct{}func (e *ElementB) Accept(visitor Visitor) {visitor.VisitElementB(e)
}// 具体访问者
type ConcreteVisitor struct{}func (cv *ConcreteVisitor) VisitElementA(e *ElementA) {fmt.Println("Visiting ElementA")
}func (cv *ConcreteVisitor) VisitElementB(e *ElementB) {fmt.Println("Visiting ElementB")
}func main() {elements := []Element{&ElementA{}, &ElementB{}}visitor := &ConcreteVisitor{}for _, element := range elements {element.Accept(visitor)}
}

三、总结

Listener模式和Visitor模式虽然都是行为设计模式,但它们解决的问题和应用场景有所不同。

  • Listener模式主要用于事件驱动的编程,适用于需要在特定事件发生时通知多个监听器的场景。它通过解耦事件源和监听器,使得系统更加灵活和可扩展。

  • Visitor模式则用于在不改变对象结构的前提下,定义新的操作。它通过双重分派机制,使得新的操作可以很容易地添加,适用于需要对复杂对象结构进行操作的场景。

通过本文的解释和代码示例,希望你能更好地理解这两种设计模式的区别和应用场景。在实际开发中,根据具体需求选择合适的设计模式,可以提高代码的可维护性和扩展性。

相关文章:

设计模式】Listener模式和Visitor模式的区别

文章目录 前言一、介绍Listener模式Visitor模式 二、代码实现2.1 Listener模式的Java实现2.2Listener模式的Go实现2.3Visitor模式的Java实现2.4Visitor模式的Go实现 三、总结 前言 在软件设计中&#xff0c;设计模式是解决特定问题的通用解决方案。Listener模式和Visitor模式是…...

基于事件序列的数据获取

Data Get 31670 /S Update 2 AI_PC 3Mins /次 Import "Hggw" PI Data AABB020240908_115221_31781 AABB020240908_115521_31781 AABB020240908_115821_31781 1、From PIdata Copy 2 AI PC 2、AI PC UI Chart & Logic OK NG Pump&#xff1a;&#x…...

太速科技-基于XC7Z100+AD9361的双收双发无线电射频板卡

基于XC7Z100AD9361的双收双发无线电射频板卡 一、板卡概述 基于XC7Z100AD9361的双收双发无线电射频板卡是基于Xilinx ZYNQ FPGA和ADI的无线收发芯片AD9361开发的专用功能板卡&#xff0c;用于4G小基站&#xff0c;无线图传&#xff0c;数据收发等领域。 二、板卡…...

探索UWB技术的独特优势:实现高精度定位

UWB定位技术是一种利用无线信号进行精确位置定位的技术&#xff0c;它利用超宽带无线电信号通过测量信号的到达时间、相位差和信号能量等参数来确定物体的精确位置。 UWB定位技术具有多种优势&#xff0c;首先&#xff0c;它具有较高的定位精度&#xff0c;可实现毫米级的精确…...

软件安装攻略:Sublime Text 下载安装和使用教程

Sublime Text 下载安装和使用教程 Sublime Text是一个流行的跨平台文本编辑器&#xff0c;它具有以下一些主要功能和特点&#xff1a; &#xff08;1&#xff09;简洁的界面和快速的速度&#xff1a;Sublime Text拥有简约干净的界面&#xff0c;启动和响应速度很快。 &#…...

ip地址为什么要轮换

在网络世界中&#xff0c;IP地址是设备与互联网通信的身份证。然而&#xff0c;单一的IP地址可能会因为各种原因而需要轮换。IP轮换是指在一定时间内更换正在使用的IP地址&#xff0c;这一策略在多种网络应用中发挥着重要作用。本文将探讨IP地址轮换的原因、其带来的优势以及实…...

C++ 继承【一篇让你学会继承】

1. 继承的概念及定义 1.1 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特征的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构&…...

DeviceNet网关HT3S-DNS-MDN读取七星华创CS310空气流量计数据应用案例

七星华创流量计CS310系列 (MODBUS RTU) 通过DeviceNet网关HT3S-DNS-MDN 与台达DVP系列的PLC进行交换数据应用案例 一、概述 本文主要介绍使用HI-TOP网关 HT3S-DNS-MDN在台达DVP系列 PLC和七星华创CS310流量计之间进行数据交换。 解决的问题&#xff1a;台达DVP系列如何通过…...

Smartbi体验中心新增系列Demo,用户体验更丰富

为进一步提升用户体验&#xff0c;让大家更直观地了解Smartbi产品在数据分析方面的功能优势&#xff0c;Smartbi体验中心近期新增了一系列Demo。这些更新旨在优化产品操作流程&#xff0c;并为用户提供更多真实场景下的应用参考。接下来&#xff0c;我们一起简要浏览此次体验中…...

Kubernetes 与 springboot集成

Kubernetes 与 Spring Boot 集成详解 Kubernetes&#xff08;简称 K8s&#xff09;是一个用于自动化部署、扩展和管理容器化应用的开源平台&#xff0c;而 Spring Boot 是 Java 开发领域中非常流行的微服务框架。将这两者结合&#xff0c;可以充分利用 Kubernetes 强大的容器编…...

以太网传输出现不分包

最近对手件反馈,在传输文件的时候,我们这边发包太快,导致对手件网络出现了拥塞,把他们程序给搞死了。他们抓了一下他们收到的包,发现我们发送的数据包都大于了MTU设置的值。现在被要求更改。 排查方法:为什么我们发送的数据包会大于MTU的值。 可能性一:配置了Dont Fra…...

[实践应用] 深度学习之激活函数

文章总览&#xff1a;YuanDaiMa2048博客文章总览 深度学习之激活函数 激活函数基本概念分类常见的激活函数2. Tanh/双曲正切激活函数3. ReLU激活函数4. Softmax激活函数 PyTorch中如何使用1. 线性激活函数2. 非线性激活函数SigmoidTanhReLULeaky ReLUParametric ReLU (PReLU) 使…...

Java基础之数组

文章地址&#xff1a;Java基础之数组 码农爱刷题 为计算机编程爱好者和从业人士提供技术总结和分享 &#xff01;为前行者蓄力&#xff0c;为后来者探路&#xff01;...

基于SpringBoot+Vue的智慧自习室预约管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…...

pptpd配置文件/etc/pptpd.conf详解

正文共&#xff1a;1111 字 2 图&#xff0c;预估阅读时间&#xff1a;1 分钟 如果要在Linux系统配置PPTP&#xff08;Point-to-Point Tunneling Protocol&#xff0c;点到点隧道协议&#xff09;VPN&#xff0c;一般是使用pptpd软件。pptpd命令通常从配置文件/etc/pptpd.conf中…...

springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件

首先&#xff0c;对数据库进行备份&#xff0c;用到的命令&#xff1a; mysqldump --opt -h 192.168.1.200 --userroot --passwordxxx --result-fileE://data//20240911141400.sql --default-character-setutf8 xxx&#xff08;数据库名&#xff09; 直接上代码 配置文件部分…...

【软考中级攻略站】-软件设计师(4)-计算机网络基础

计算机网络的分类 1. 局域网&#xff08;Local Area Network, LAN&#xff09; 局域网是指在一个较小的地理区域内连接的计算机网络&#xff0c;比如学校的机房、公司的办公室或者家里的Wi-Fi网络。在这个网络内的计算机可以直接相互通信&#xff0c;速度很快&#xff0c;延迟…...

Android以及IoT设备传感器软件开发总结

1 传感器选型 1.1 传感器选型 6 axis&#xff1a;Bosch BMI160&#xff08;比较差&#xff09;&#xff0c;InvenSense MPU6050&#xff08;DMP&#xff09;&#xff0c;ST LSM6Dxx Acc: Freescale MMA7450L (MicroMachined Accelerometer/MC Multi-Axis, iMX31), Kionix KXSD9…...

Vue2/Vue3中编程式路由导航实践总结

【1】Vue2编程式路由导航 ① router.push 除了使用 <router-link> 创建 a 标签来定义导航链接&#xff0c;我们还可以借助 router 的实例方法&#xff0c;通过编写代码来实现。 router.push(location, onComplete?, onAbort?)注意&#xff1a;在 Vue 实例内部&#…...

【nginx】ngx_http_proxy_connect_module 正向代理

50.65无法访问 服务器, (403 错误) 50.196 可以访问服务器。 那么,配置65 通过196 访问。 需要一个nginx作为代理 【nginx】搭配okhttp 配置反向代理 发送原生的nginx是不支持okhttp的CONNECT请求的。 大神竟然给出了一个java工程 GINX编译ngx_http_proxy_connect_module及做…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

二维FDTD算法仿真

二维FDTD算法仿真&#xff0c;并带完全匹配层&#xff0c;输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...