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

设计模式-访问者模式(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)

设计模式-访问者模式&#xff08;Visitor&#xff09; 一、访问者模式概述1.1 什么是访问者模式1.2 简单实现访问者模式1.3 使用访问者模式的注意事项 二、访问者模式的用途三、访问者模式实现方式3.1 递归遍历实现访问者模式3.2 迭代遍历实现访问者模式3.3 Java8 Stream API 实…...

C++二分查找算法:132 模式解法二枚举2

题目及解法一&#xff1a; https://blog.csdn.net/he_zhidan/article/details/134362273 分析 第一步&#xff0c;选择各3对应的1&#xff0c;如果有多个符合对应最小的1&#xff0c;记录num[0,j)中的最小值iMin&#xff0c;如果nums[j]大于iMin&#xff0c;则m3To1 [nums[j…...

JavaWeb-HTML

​ 一、什么是HTML HTML是hypertext markup language&#xff08;超文本标记语言&#xff09;的缩写。HTML文件本质上是文本文件&#xff0c;普通的文本文件只能显示字符&#xff0c;而HTML文件可以在浏览器上显示更丰富的信息&#xff08;如图片等&#xff09;。 超文本&am…...

新外卖霸王餐小程序、H5、微信公众号版外卖系统源码

最新外卖霸王餐小程序、H5、微信公众号版外卖系统源码、霸王餐美团、饿了么系统&#xff0c;粉丝裂变玩源码下载&#xff0c;外卖cps小程序项目&#xff0c;外卖红包cps带好友返利佣金分销系统程序、饿了么美团联盟源码&#xff0c;外卖cps带分销返利后端源码&#xff0c;基于L…...

LeetCode - #89 格雷编码

文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅&#xff08;Netflix 增长黑客&#xff0c;《iOS 面试之道》作者&#xff0c;ACE 职业健身教练。&#xff09;的 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 数组指针变量是什么&#xff1f;2.2 数组指针变量怎么初始化? 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 两段有趣的代码4.3.1 typedef关键字 5. 函数指针数组6. 转移…...

Rust图形界面编程:egui平直布局

文章目录 平直布局with_layout 平直布局 在前面的示例中&#xff0c;已经用到了ui.horizontal用来布局&#xff0c;其特点是水平摆放控件。相应地&#xff0c;ui.vertical则是垂直摆放控件。根据控件的摆放顺序不同&#xff0c;这两个布局组件衍生出一系列布局函数 horizonta…...

Android13 wifi adb 串口开启

Android13 wifi adb 串口开启 文章目录 Android13 wifi adb 串口开启一、前言二、开启wifi adb1、开启wifi adb 命令&#xff1a;2、查看和设置 adb默认值3、adb 开启属性prop和settings属性的关系 三、总结1、Android13 开启adb 串口命令2、Android 13 wifi adb设置固定端口解…...

关于一个屏幕取词程序,AI给的创建思路及指导

我&#xff1a;我在windows上&#xff0c;经常碰到各种软件当中有自己不认识的英文&#xff0c;请问如果要用python开发一个随时添加屏幕上任意英文单词到生词词典中的软件&#xff0c;该怎么进行&#xff1f; AI&#xff1a;开发一个能够从屏幕上捕获英文单词并将其添加到生词…...

MySql跨库跨表触发器

一、跨库触发器的概念 跨库触发器是指能在一个数据库中创建的触发器&#xff0c;但触发器的操作涉及到其他数据库中的表。这种触发器的存在可以帮助我们实现一些复杂的业务逻辑&#xff0c;比如在一个数据库中的表更新时&#xff0c;自动更新另一个数据库中的相关表。 二、创建…...

NextJS开发:shadcn/ui中Button组件扩展增加图标

shadcn/ui组件比较灵活&#xff0c;但是功能相比ant之类组件还是缺少太多功能&#xff0c;本文为shadcn/ui为button组件增加图标&#xff0c;加载中动画等效果。 安装Lucide npm install lucideLucide组件 import { cn } from /lib/utils; import { icons } from lucide-rea…...

Go 语言

1. 请简要介绍一下 Go 语言的特点。 Go 语言是一种高性能、并发支持强大且易于学习的编程语言。以下是 Go 语言的一些主要特点&#xff1a; 高性能&#xff1a;Go 语言的运行速度接近 C 和 Java&#xff0c;某些场景下甚至更快&#xff0c;这使得它非常适合用于高性能计算和网…...

【计算机网络笔记】DHCP协议

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…...

21 Linux 自带的LED驱动

一、Linux 自带 LED 驱动使能 其实 Linux 内核自带 LED 抢夺那个&#xff0c;但在此之前需要配置 Linux 驱动来使能 LED 驱动。 输入以下命令&#xff1a; cd linux/atk-mpl/linux/my_linux/linux-5.4.31 make menuconfig 根据以下路径找到 LED 驱动&#xff1a; → Device D…...

神通MPP数据库的跨库查询

神通MPP数据库的跨库查询 一. 简介二. 系统表三. 跨库查询语法1. 创建外部数据存储服务器2. 删除外部数据存储服务器3. 授予普通用户访问外部数据存储服务器权限4. 回收普通用户访问外部数据存储服务器权限5. 加密函数6. 访问外部数据存储服务器 ★ 四. 跨库查询&#xff1a;统…...

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坐标关系和应用

前言&#xff1a; 我们需要先由一个 QGraphicsView&#xff0c;这个是UI显示的地方&#xff0c;也就是装满可见原色的Scene&#xff0c;然后需要一个QGraphicsScene 用来管理所有可见的界面元素&#xff0c;要实现UI功能&#xff0c;我们需要用各种从QGraphicsItem拼装成UI控件…...

32 _ 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?

从今天开始,我们来学习字符串匹配算法。字符串匹配这样一个功能,我想对于任何一个开发工程师来说,应该都不会陌生。我们用的最多的就是编程语言提供的字符串查找函数,比如Java中的indexOf(),Python中的find()函数等,它们底层就是依赖接下来要讲的字符串匹配算法。 字符串…...

TCP怎么实现可靠传输

链接 1&#xff0c;TCP头部的校验和保证获取正确数据&#xff0c;防篡改&#xff1b; 2&#xff0c;序列号和ACK确认机制同于管理数据包&#xff0c;对接收到的数据包进行确认&#xff0c;对没有接收到的数据包进行重传&#xff1b; 3&#xff0c;重传机制&#xff0c;包括超…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...