设计模式】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实现 三、总结 前言 在软件设计中,设计模式是解决特定问题的通用解决方案。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:&#x…...
太速科技-基于XC7Z100+AD9361的双收双发无线电射频板卡
基于XC7Z100AD9361的双收双发无线电射频板卡 一、板卡概述 基于XC7Z100AD9361的双收双发无线电射频板卡是基于Xilinx ZYNQ FPGA和ADI的无线收发芯片AD9361开发的专用功能板卡,用于4G小基站,无线图传,数据收发等领域。 二、板卡…...
探索UWB技术的独特优势:实现高精度定位
UWB定位技术是一种利用无线信号进行精确位置定位的技术,它利用超宽带无线电信号通过测量信号的到达时间、相位差和信号能量等参数来确定物体的精确位置。 UWB定位技术具有多种优势,首先,它具有较高的定位精度,可实现毫米级的精确…...
软件安装攻略:Sublime Text 下载安装和使用教程
Sublime Text 下载安装和使用教程 Sublime Text是一个流行的跨平台文本编辑器,它具有以下一些主要功能和特点: (1)简洁的界面和快速的速度:Sublime Text拥有简约干净的界面,启动和响应速度很快。 &#…...
ip地址为什么要轮换
在网络世界中,IP地址是设备与互联网通信的身份证。然而,单一的IP地址可能会因为各种原因而需要轮换。IP轮换是指在一定时间内更换正在使用的IP地址,这一策略在多种网络应用中发挥着重要作用。本文将探讨IP地址轮换的原因、其带来的优势以及实…...
C++ 继承【一篇让你学会继承】
1. 继承的概念及定义 1.1 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特征的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构&…...
DeviceNet网关HT3S-DNS-MDN读取七星华创CS310空气流量计数据应用案例
七星华创流量计CS310系列 (MODBUS RTU) 通过DeviceNet网关HT3S-DNS-MDN 与台达DVP系列的PLC进行交换数据应用案例 一、概述 本文主要介绍使用HI-TOP网关 HT3S-DNS-MDN在台达DVP系列 PLC和七星华创CS310流量计之间进行数据交换。 解决的问题:台达DVP系列如何通过…...
Smartbi体验中心新增系列Demo,用户体验更丰富
为进一步提升用户体验,让大家更直观地了解Smartbi产品在数据分析方面的功能优势,Smartbi体验中心近期新增了一系列Demo。这些更新旨在优化产品操作流程,并为用户提供更多真实场景下的应用参考。接下来,我们一起简要浏览此次体验中…...
Kubernetes 与 springboot集成
Kubernetes 与 Spring Boot 集成详解 Kubernetes(简称 K8s)是一个用于自动化部署、扩展和管理容器化应用的开源平台,而 Spring Boot 是 Java 开发领域中非常流行的微服务框架。将这两者结合,可以充分利用 Kubernetes 强大的容器编…...
以太网传输出现不分包
最近对手件反馈,在传输文件的时候,我们这边发包太快,导致对手件网络出现了拥塞,把他们程序给搞死了。他们抓了一下他们收到的包,发现我们发送的数据包都大于了MTU设置的值。现在被要求更改。 排查方法:为什么我们发送的数据包会大于MTU的值。 可能性一:配置了Dont Fra…...
[实践应用] 深度学习之激活函数
文章总览:YuanDaiMa2048博客文章总览 深度学习之激活函数 激活函数基本概念分类常见的激活函数2. Tanh/双曲正切激活函数3. ReLU激活函数4. Softmax激活函数 PyTorch中如何使用1. 线性激活函数2. 非线性激活函数SigmoidTanhReLULeaky ReLUParametric ReLU (PReLU) 使…...
Java基础之数组
文章地址:Java基础之数组 码农爱刷题 为计算机编程爱好者和从业人士提供技术总结和分享 !为前行者蓄力,为后来者探路!...
基于SpringBoot+Vue的智慧自习室预约管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…...
pptpd配置文件/etc/pptpd.conf详解
正文共:1111 字 2 图,预估阅读时间:1 分钟 如果要在Linux系统配置PPTP(Point-to-Point Tunneling Protocol,点到点隧道协议)VPN,一般是使用pptpd软件。pptpd命令通常从配置文件/etc/pptpd.conf中…...
springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件
首先,对数据库进行备份,用到的命令: mysqldump --opt -h 192.168.1.200 --userroot --passwordxxx --result-fileE://data//20240911141400.sql --default-character-setutf8 xxx(数据库名) 直接上代码 配置文件部分…...
【软考中级攻略站】-软件设计师(4)-计算机网络基础
计算机网络的分类 1. 局域网(Local Area Network, LAN) 局域网是指在一个较小的地理区域内连接的计算机网络,比如学校的机房、公司的办公室或者家里的Wi-Fi网络。在这个网络内的计算机可以直接相互通信,速度很快,延迟…...
Android以及IoT设备传感器软件开发总结
1 传感器选型 1.1 传感器选型 6 axis:Bosch BMI160(比较差),InvenSense MPU6050(DMP),ST LSM6Dxx Acc: Freescale MMA7450L (MicroMachined Accelerometer/MC Multi-Axis, iMX31), Kionix KXSD9…...
Vue2/Vue3中编程式路由导航实践总结
【1】Vue2编程式路由导航 ① router.push 除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。 router.push(location, onComplete?, onAbort?)注意:在 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及做…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
