设计模式】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及做…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...