设计模式-访问者模式(Visitor)
设计模式-访问者模式(Visitor)
- 一、访问者模式概述
- 1.1 什么是访问者模式
- 1.2 简单实现访问者模式
- 1.3 使用访问者模式的注意事项
- 二、访问者模式的用途
- 三、访问者模式实现方式
- 3.1 递归遍历实现访问者模式
- 3.2 迭代遍历实现访问者模式
- 3.3 Java8 Stream API 实现访问者模式
一、访问者模式概述
1.1 什么是访问者模式
访问者模式(Visitor Pattern)是一种将算法与对象结构分离的软件设计模式。它的基本思想是让访问者对象能够遍历一个或多个被访问对象,并根据需要对它们执行操作。
在访问者模式中,被访问对象通常有一个接受访问者的方法,该方法接受一个访问者对象作为参数。访问者对象则定义了一个用于访问被访问对象的接口,该接口包含一组方法,每个方法对应于被访问对象的一个操作。
访问者模式的优点包括:
-
1、将被访问对象和访问者解耦,使得它们可以独立地变化而不影响彼此;
-
2、支持递归遍历,使得访问者可以处理复杂的数据结构;
-
3、可以很容易地添加新的操作到被访问对象和访问者中,而不需要修改现有的代码。
访问者模式的缺点包括: -
1、如果被访问对象和访问者的接口发生变化,可能需要修改大量的代码;
-
2、如果被访问对象和访问者的数量很大,可能会导致系统的性能下降。
1.2 简单实现访问者模式
首先,我们定义一个访问者接口Visitor,它包含一个visit方法,用于访问不同类型的元素:
public interface Visitor {void visit(ElementType1 element);void visit(ElementType2 element);// ...其他元素类型
}
然后,我们创建一个具体的访问者类ConcreteVisitor,实现Visitor接口,并重写visit方法以执行相应的操作:
public class ConcreteVisitor implements Visitor {@Overridepublic void visit(ElementType1 element) {// 对ElementType1类型的元素执行操作System.out.println("处理ElementType1类型的元素");}@Overridepublic void visit(ElementType2 element) {// 对ElementType2类型的元素执行操作System.out.println("处理ElementType2类型的元素");}// ...其他元素类型的处理方法
}
接下来,我们定义一个元素接口Element,它包含一个accept方法,用于接受访问者:
public interface Element {void accept(Visitor visitor);
}
然后,我们创建一些具体的元素类,实现Element接口,并重写accept方法以调用访问者的visit方法:
public class ConcreteElementType1 implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}public class ConcreteElementType2 implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// ...其他元素类型的实现
最后,我们可以在主程序中使用访问者模式来处理元素:
public class Main {public static void main(String[] args) {Visitor visitor = new ConcreteVisitor();Element element1 = new ConcreteElementType1();Element element2 = new ConcreteElementType2();element1.accept(visitor);element2.accept(visitor);}
}
运行上述代码,将输出:
处理ElementType1类型的元素
处理ElementType2类型的元素
1.3 使用访问者模式的注意事项
-
1、确定何时使用访问者模式:访问者模式适用于需要对一组对象进行操作,而这些对象的操作又不相同的情况。如果对象之间的操作相同,则应该考虑使用其他设计模式。
-
2、将对象和操作分离:访问者模式的核心思想是将对象和操作分离,使得可以独立地改变它们。因此,在实现访问者模式时,必须确保对象和操作的分离。
-
3、避免滥用递归:访问者模式通常使用递归来遍历对象结构。但是,如果对象结构过于复杂或递归深度过大,可能会导致性能问题。因此,在使用访问者模式时,应该避免滥用递归。
-
4、考虑使用其他设计模式:访问者模式是一种较为复杂的设计模式,使用时需要考虑其优缺点。在某些情况下,可以考虑使用其他更简单的设计模式来解决问题。
-
5、保持代码简洁:访问者模式可能会使代码变得复杂,因此应该尽可能地保持代码简洁。可以通过使用辅助类、封装访问者逻辑等方式来实现这一点。
二、访问者模式的用途
访问者模式是一种将作用于某种数据结构中各元素的操作分离出来封装成独立的类,使其访问者模式是一种将作用于某种数据结构中各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作的设计模式。它主要适用于对一组对象进行操作,而这些对象的操作又不相同的情况。
以下是访问者模式的主要应用场景:
-
1、对象结构相对稳定,但其操作算法经常变化的程序。这是因为访问者模式能够分离出针对对象的操作,当需要改变操作时,只需更改访问者类的代码,而无需修改对象结构的代码。
-
2、对象结构复杂,且需要对每一个对象执行不同的操作。如果直接在对象上执行操作,可能会让代码变得复杂难以理解。使用访问者模式可以将复杂的操作分解为多个访问者类,使得代码更加清晰和易于维护。
-
3、需要遍历一个复杂的对象结构。访问者模式通常使用递归来遍历对象结构,如果对象结构过于复杂或递归深度过大,可能会导致性能问题。但在一些情况下,如医院药单处理系统等,访问者模式可以有效地简化并优化遍历过程。
总的来说,访问者模式是一种强大的设计模式,它提供了一种灵活可扩展的方式来处理复杂的对象结构和操作,但同时也需要注意其复杂性和可能的性能问题。
三、访问者模式实现方式
3.1 递归遍历实现访问者模式
递归遍历实现访问者模式的步骤如下:
- 1、定义一个访问者接口,包含访问元素的方法。
- 2、定义一个元素类,包含一个访问者列表和一个接受访问者的方法。
- 3、在元素类中实现递归遍历方法,遍历子元素并调用它们的访问方法。
- 4、创建一个具体的访问者类,实现访问者接口。
- 5、创建一个元素对象,添加子元素和访问者。
- 6、调用元素的递归遍历方法。
以下是一个简单的示例:
// 访问者接口
interface Visitor {void visit(Element element);
}// 元素类
class Element {private List<Element> children = new ArrayList<>();private Visitor visitor;public void accept(Visitor visitor) {this.visitor = visitor;visitor.visit(this);for (Element child : children) {child.accept(visitor);}}public void addChild(Element child) {children.add(child);}
}// 具体的访问者类
class ConcreteVisitor implements Visitor {@Overridepublic void visit(Element element) {System.out.println("访问元素: " + element);}
}public class Main {public static void main(String[] args) {Element root = new Element();Element child1 = new Element();Element child2 = new Element();root.addChild(child1);root.addChild(child2);Visitor visitor = new ConcreteVisitor();root.accept(visitor);}
}
这个示例中,我们创建了一个元素类Element,它包含一个访问者列表和一个接受访问者的方法。我们还创建了一个具体的访问者类ConcreteVisitor,实现了访问者接口。在主方法中,我们创建了一个元素对象,添加了子元素和访问者,然后调用了元素的递归遍历方法。
3.2 迭代遍历实现访问者模式
- 1、定义一个访问者接口,包含访问元素的方法。
- 2、定义一个元素类,包含一个访问者列表和一个接受访问者的方法。
- 3、在元素类中实现迭代遍历方法,遍历子元素并调用它们的访问方法。
- 4、创建一个具体的访问者类,实现访问者接口。
- 5、创建一个元素对象,添加子元素和访问者。
- 6、调用元素的迭代遍历方法。
// 访问者接口
interface Visitor {void visit(Element element);
}// 元素类
class Element {private List<Element> children = new ArrayList<>();private Visitor visitor;public void accept(Visitor visitor) {this.visitor = visitor;visitor.visit(this);Iterator<Element> iterator = children.iterator();while (iterator.hasNext()) {Element child = iterator.next();child.accept(visitor);}}public void addChild(Element child) {children.add(child);}
}// 具体的访问者类
class ConcreteVisitor implements Visitor {@Overridepublic void visit(Element element) {System.out.println("访问元素:" + element);}
}public class Main {public static void main(String[] args) {Element root = new Element();Element child1 = new Element();Element child2 = new Element();root.addChild(child1);root.addChild(child2);Visitor visitor = new ConcreteVisitor();root.accept(visitor);}
}
这个示例中,我们创建了一个元素类Element,它包含一个访问者列表和一个接受访问者的方法。我们还创建了一个具体的访问者类ConcreteVisitor,实现了访问者接口。在主方法中,我们创建了一个元素对象,添加了子元素和访问者,然后调用了元素的迭代遍历方法
3.3 Java8 Stream API 实现访问者模式
要使用Java 8 Stream API实现访问者模式,首先需要定义一个访问者接口,然后创建一个具体的访问者类实现该接口。接下来,使用Stream API对集合进行操作,并在操作过程中调用访问者的相应方法。以下是一个简单的示例:
定义访问者接口:
public interface Visitor {void visit(Element element);
}
创建具体的访问者类实现访问者接口:
public class ConcreteVisitor implements Visitor {@Overridepublic void visit(Element element) {// 在这里处理元素的逻辑System.out.println("访问元素: " + element);}
}
使用Stream API对集合进行操作,并在操作过程中调用访问者的相应方法:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class Main {public static void main(String[] args) {List<Element> elements = Arrays.asList("A", "B", "C", "D");// 使用Stream API对集合进行操作,并在操作过程中调用访问者的相应方法List<Element> result = elements.stream().map(element -> {System.out.println("处理元素: " + element);return element;}).collect(Collectors.toList());System.out.println("处理后的元素列表: " + result);}
}
在这个示例中,我们首先定义了一个访问者接口Visitor,然后创建了一个具体的访问者类ConcreteVisitor实现了该接口。在main方法中,我们使用Stream API对一个元素列表进行操作,并在操作过程中调用访问者的visit方法。
相关文章:
设计模式-访问者模式(Visitor)
设计模式-访问者模式(Visitor) 一、访问者模式概述1.1 什么是访问者模式1.2 简单实现访问者模式1.3 使用访问者模式的注意事项 二、访问者模式的用途三、访问者模式实现方式3.1 递归遍历实现访问者模式3.2 迭代遍历实现访问者模式3.3 Java8 Stream API 实…...
C++二分查找算法:132 模式解法二枚举2
题目及解法一: https://blog.csdn.net/he_zhidan/article/details/134362273 分析 第一步,选择各3对应的1,如果有多个符合对应最小的1,记录num[0,j)中的最小值iMin,如果nums[j]大于iMin,则m3To1 [nums[j…...
JavaWeb-HTML
一、什么是HTML HTML是hypertext markup language(超文本标记语言)的缩写。HTML文件本质上是文本文件,普通的文本文件只能显示字符,而HTML文件可以在浏览器上显示更丰富的信息(如图片等)。 超文本&am…...
新外卖霸王餐小程序、H5、微信公众号版外卖系统源码
最新外卖霸王餐小程序、H5、微信公众号版外卖系统源码、霸王餐美团、饿了么系统,粉丝裂变玩源码下载,外卖cps小程序项目,外卖红包cps带好友返利佣金分销系统程序、饿了么美团联盟源码,外卖cps带分销返利后端源码,基于L…...
LeetCode - #89 格雷编码
文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新…...
11.3SpringMVC
一.概念 1.SpringMvc: a.构建在Servlet(api)基础上. b.是一个Web框架(HTTP). c.来自于Spring webMVC模块. 2.MVC 二.注册路由的注解 1.RequestMapping("/test") // 路由注册 注意: 这个注解在类和方法上都要使用,代表不同等级的路由. 2.RestController a)R…...
c语言从入门到实战——数组指针与函数指针
数组指针与函数指针 前言1. 字符指针变量2. 数组指针变量2.1 数组指针变量是什么?2.2 数组指针变量怎么初始化? 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 两段有趣的代码4.3.1 typedef关键字 5. 函数指针数组6. 转移…...
Rust图形界面编程:egui平直布局
文章目录 平直布局with_layout 平直布局 在前面的示例中,已经用到了ui.horizontal用来布局,其特点是水平摆放控件。相应地,ui.vertical则是垂直摆放控件。根据控件的摆放顺序不同,这两个布局组件衍生出一系列布局函数 horizonta…...
Android13 wifi adb 串口开启
Android13 wifi adb 串口开启 文章目录 Android13 wifi adb 串口开启一、前言二、开启wifi adb1、开启wifi adb 命令:2、查看和设置 adb默认值3、adb 开启属性prop和settings属性的关系 三、总结1、Android13 开启adb 串口命令2、Android 13 wifi adb设置固定端口解…...
关于一个屏幕取词程序,AI给的创建思路及指导
我:我在windows上,经常碰到各种软件当中有自己不认识的英文,请问如果要用python开发一个随时添加屏幕上任意英文单词到生词词典中的软件,该怎么进行? AI:开发一个能够从屏幕上捕获英文单词并将其添加到生词…...
MySql跨库跨表触发器
一、跨库触发器的概念 跨库触发器是指能在一个数据库中创建的触发器,但触发器的操作涉及到其他数据库中的表。这种触发器的存在可以帮助我们实现一些复杂的业务逻辑,比如在一个数据库中的表更新时,自动更新另一个数据库中的相关表。 二、创建…...
NextJS开发:shadcn/ui中Button组件扩展增加图标
shadcn/ui组件比较灵活,但是功能相比ant之类组件还是缺少太多功能,本文为shadcn/ui为button组件增加图标,加载中动画等效果。 安装Lucide npm install lucideLucide组件 import { cn } from /lib/utils; import { icons } from lucide-rea…...
Go 语言
1. 请简要介绍一下 Go 语言的特点。 Go 语言是一种高性能、并发支持强大且易于学习的编程语言。以下是 Go 语言的一些主要特点: 高性能:Go 语言的运行速度接近 C 和 Java,某些场景下甚至更快,这使得它非常适合用于高性能计算和网…...
【计算机网络笔记】DHCP协议
系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…...
21 Linux 自带的LED驱动
一、Linux 自带 LED 驱动使能 其实 Linux 内核自带 LED 抢夺那个,但在此之前需要配置 Linux 驱动来使能 LED 驱动。 输入以下命令: cd linux/atk-mpl/linux/my_linux/linux-5.4.31 make menuconfig 根据以下路径找到 LED 驱动: → Device D…...
神通MPP数据库的跨库查询
神通MPP数据库的跨库查询 一. 简介二. 系统表三. 跨库查询语法1. 创建外部数据存储服务器2. 删除外部数据存储服务器3. 授予普通用户访问外部数据存储服务器权限4. 回收普通用户访问外部数据存储服务器权限5. 加密函数6. 访问外部数据存储服务器 ★ 四. 跨库查询:统…...
JavaWeb-WEB请求过程
WEB请求过程 一、B/S架构1.1 BS结构的好处1.2 B/S架构是如何完成交互的1.3 B/S网络架构的核心HTTP1.3.1 HTTP请求头1.3.2 HTTP响应头1.3.3 HTTP状态码1.3.4 HTTP缓存机制二、DNS域名解析、CND(分发网络)、负载均衡2.1 DNS域名解析2.2 CDN工作机制2.3 负载均衡2.3.1 硬件负载均衡…...
《QT从基础到进阶·二十一》QGraphicsView、QGraphicsScene和QGraphicsItem坐标关系和应用
前言: 我们需要先由一个 QGraphicsView,这个是UI显示的地方,也就是装满可见原色的Scene,然后需要一个QGraphicsScene 用来管理所有可见的界面元素,要实现UI功能,我们需要用各种从QGraphicsItem拼装成UI控件…...
32 _ 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?
从今天开始,我们来学习字符串匹配算法。字符串匹配这样一个功能,我想对于任何一个开发工程师来说,应该都不会陌生。我们用的最多的就是编程语言提供的字符串查找函数,比如Java中的indexOf(),Python中的find()函数等,它们底层就是依赖接下来要讲的字符串匹配算法。 字符串…...
TCP怎么实现可靠传输
链接 1,TCP头部的校验和保证获取正确数据,防篡改; 2,序列号和ACK确认机制同于管理数据包,对接收到的数据包进行确认,对没有接收到的数据包进行重传; 3,重传机制,包括超…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
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 的密码…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
