23、设计模式之访问者模式(Visitor)

news2024/11/20 12:40:16

一、什么是访问者模式
访问者模式是一种行为型设计模式,它可以用于在不修改已有对象结构的情况下,定义新的操作方式。简单地说就是在不改变数据结构的前提下,通过在数据结构中加入一个新的角色——访问者,来达到执行不同操作的目的。

二、角色组成

抽象访问者(Visitor):定义了对自身数据结构中各个元素的操作,为每个具体元素类对应一个访问操作,该操作中的参数标识了被访问的具体元素。
具体访问者(Concrete Visitor):实现了访问者接口中定义的具体操作,确定访问者访问一个元素时该做什么。
抽象元素(Element):定义了接受访问者的接口,通常是一个接口或抽象类,其中定义了一个接受访问者的方法,被访问者对象作为方法的参数。
具体元素(Concrete Element):实现了抽象元素接口,它是数据结构中的具体的元素,用于接收具体的访问者并执行相应的操作。每个具体元素都有自己的业务逻辑,并在接收访问者时将具体的操作委托给访问者进行处理。
对象结构(Object Structure):是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

三、优缺点
优点:

扩展性好:能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
复用性好:可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
灵活性好:访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点:

违反开闭原则:在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
违反迪米特法则:在访问者模式中,具体元素类需要暴露接受访问者的方法给访问者使用,这样破坏了具体元素类对于访问者的封装性,增加了元素类与具体访问者之间的耦合性。
破坏封装性:访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
四、 应用场景
4.1 生活场景
蔬菜摊位:在一个农贸市场的蔬菜摊位上,摊主可能提供不同的服务,如称重、清洗、切割等。每个服务可以被视为一个访问者,而蔬菜则是元素,根据不同的服务访问者进行相应的操作。
医院就诊:在医院就诊时,医生、护士和药师可以视为不同的访问者,而病人则是元素。不同的访问者根据其职责和需要,对病人进行不同的操作和处理,如诊断、治疗、开药等。
旅游参观:游客可以参观不同的地方,如参观文物、欣赏风景等。游客可以看作访问者,而景点本身则是元素,根据游客的选择展示不同的参观操作。
4.2 Java场景
XML解析器:许多Java XML解析器,如DOM和SAX解析器,使用访问者模式来处理XML文档。XML的节点可以被视为元素,而访问者则可以是处理XML节点的具体操作,如提取数据、修改节点等。
数据库访问:一些Java数据库访问框架,如Hibernate和MyBatis,使用访问者模式来处理数据库操作。数据库表和字段可以被视为元素,而访问者则可以是查询、更新或删除操作,通过访问者模式来执行这些操作。
文件系统操作:Java的文件和目录操作中,使用了访问者模式。Java提供了FileVisitor接口和SimpleFileVisitor类,可以通过自定义的访问者类来遍历文件系统,并执行不同的操作,如复制文件、删除文件等。
五、代码实现
下面以“旅游参观”为例,解释一下访问者模式。游客挑选不同的景点来参观,如参观文物、欣赏风景等。那么游客可以看作访问者,而景点本身则是元素,根据游客的选择展示不同的参观操作。

抽象访问者:Visitor
具体访问者:Tourist
抽象元素:Spot
具体元素:Relic、View
对象结构:SpotCollection

5.0 UML类图
在这里插入图片描述
5.1 抽象访问者——Visitor

/**
 *
 * 1.抽象访问者(Visitor):参观者
 * 定义:定义了对自身数据结构中各个元素的操作,为每个具体元素类对应一个访问操作,该操作中的参数标识了被访问的具体元素。
 */
public interface Visitor {
    void visit(Relic relic);
    void visit(View scenery);
}

5.2 具体访问者——Tourist

/**
 *
 * 2.具体访问者(Concrete Visitor):游客
 * 定义:实现了访问者接口中定义的具体操作,确定访问者访问一个元素时该做什么。
 */
public class Tourist implements Visitor{
    @Override
    public void visit(Relic relic) {
        System.out.println("游客正在参观文物...");
        relic.display();
    }
 
    @Override
    public void visit(View scenery) {
        System.out.println("游客正在欣赏自然风景...");
        scenery.display();
    }
}

5.3 抽象元素——Spot

/**
 * @
 * 3.抽象元素(Element):景点
 * 定义:定义了接受访问者的接口,通常是一个接口或抽象类,其中定义了一个接受访问者的方法,被访问者对象作为方法的参数。
 */
public interface Spot {
    void accept(Visitor visitor);
}

5.4 具体元素——View、Relic

/**
 *
 * 4.具体元素(Concrete Element):风景
 * 定义:实现了抽象元素接口,它是数据结构中的具体的元素,用于接收具体的访问者并执行相应的操作。
 * 每个具体元素都有自己的业务逻辑,并在接收访问者时将具体的操作委托给访问者进行处理。
 */
public class View implements Spot{
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this); // 将自身传递给访问者
    }
    // 具体自然风景的业务逻辑
    public void display() {
        System.out.println("这是一处壮丽的自然风景。");
    }
/**
 * 
 * 4.具体元素(Concrete Element):文物
 * 定义:实现了抽象元素接口,它是数据结构中的具体的元素,用于接收具体的访问者并执行相应的操作。
 * 每个具体元素都有自己的业务逻辑,并在接收访问者时将具体的操作委托给访问者进行处理。
 */
public class Relic implements Spot{
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this); // 将自身传递给访问者
    }
    // 具体文物的业务逻辑
    public void display() {
        System.out.println("这是一件珍贵的文物。");
    }
}

5.5 对象结构——SpotCollection

/**
 * 
 * 5.对象结构(Object Structure)
 * 定义:包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
 */
public class SpotCollection {
    private List<Spot> spots = new ArrayList<>();
 
    //添加元素
    public void addSpot(Spot spot) {
        spots.add(spot);
    }
 
    public void accept(Visitor visitor) {
        for (Spot spot : spots) {
            spot.accept(visitor);
        }
    }
}

5.6 testVisitor

/**
 * 
 * 访问者模式测试类
 */
@SpringBootTest
public class TestVisitor {
 
    @Test
    void testVisitor(){
        //创建对象结构
        SpotCollection spotCollection = new SpotCollection();
        //添加元素
        spotCollection.addSpot(new Relic());
        spotCollection.addSpot(new View());
 
        Tourist tourist = new Tourist();
        //景点接受游客的访问
        spotCollection.accept(tourist);
    }
}

在这里插入图片描述
六、总结
访问者模式主要用于将操作与数据结构分离,增加新的操作变得简单,但增加新的元素类可能会导致修改访问者接口和所有的具体访问者类。

以下情况,可以考虑使用访问者模式:

当对象结构中的元素类(Element)和操作类(Operation)的种类固定或者很少改变,但是需要经常新增或者修改操作时。
当需要对一个对象结构中的元素进行多种不同且无关的操作时。
当需要对不同类型的元素对象进行某种共同的处理逻辑时。
当对象结构中的元素类(Element)和操作类(Operation)的种类很多,并且相互之间的组合和嵌套可能会产生大量的代码重复时。
当需要在不修改元素对象的类结构的前提下,给元素对象新增一些操作时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1513963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GIS软件应用(二)

任务&#xff1a; 1. 正确划分渔网并裁剪出研究区域 2. 渔网与poi数据正确空间链接并统计网格内类别POI数量 步骤&#xff1a; 将南京市边界进行投影变换&#xff0c;具体看我的这篇文章&#xff1a;GIS软件应用&#xff08;一&#xff09;-CSDN博客 选择ArcToolbox中的 Cr…

开口式霍尔电流传感器助力直流配电改造

彭姝麟 Acrelpsl 1开口式霍尔电流传感器助力直流配电改造 1.1 改造要求 系统改造要求不停电进行直流系统切改&#xff0c;即在不失去直流电源的情况下进行负荷的倒出和倒入&#xff0c;改造工程难度大。针对此需求&#xff0c;可采用开口式霍尔电流传感器来解决改造项目中直流…

【PHP+代码审计】PHP基础——流程控制

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

Mock.js 基本语法与应用笔记

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

数据分析之一些Excel函数

数据分析之Excel的使用 SUM()求和SUMIF()单条件求和SUMIFS()多条件求和日期函数YEAR()提取年份MONTH()提取月份DAY()提取日DATE()函数 SUBTOTAL()求和IF()函数IF嵌套 VLOOKUP()搜索取值MATCH()返回行值或列值INDEX()定位取值 SUM()求和 SUM(number1,[number2],…) 对选中的区域…

CSS学习2

自己在工作中总是有一些自动化开发的需求&#xff0c;总是以为自己是有前端基础的&#xff0c;但是一写页面&#xff0c;布局都布不好&#xff0c;真是搞笑&#xff0c;说起来还是基本功不扎实啊&#xff0c;这里在重新复习一下&#xff0c;然后记录一下文档。后边在写两个综合…

Linux 配置ssh、scp、sftp免密登录

SSH&#xff08;Secure Shell&#xff09;是一种安全的远程登录协议&#xff0c;它使用客户端-服务器架构促进2个系统之间的安全通信&#xff0c;并允许用户远程登录服务器。在某些高可用环境下&#xff0c;服务器之间可能还需要配置免密互信&#xff0c;即基于密钥验证登录。 …

linux系统使用head和tail命令,快速切分json 格式的数据集

文章目录 介绍切分训练集切分测试集 介绍 json格式的数据集&#xff0c;每一行都是一个单独数据单元。 data.json的文件格式如下&#xff1a; {"text": "彭小军认为&#xff0c;国内银行现在走的是台湾的发卡模式&#xff0c;先通过跑马圈地再在圈的地里面选择…

产品实操——设计阶段

一、思维导图&#xff1a; 二、原型图&#xff1a; 1.墨刀&#xff1a;

【代码随想录 | 数组 05】螺旋矩阵 ||

文章目录 5.螺旋矩阵25.1题目5.2思路 5.螺旋矩阵2 5.1题目 59. 螺旋矩阵 II 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例一&#xff1a; 输入&#xff1a;n 3 输出&#xff…

OpenGL-贴纸方案

OpenGL-贴纸方案 普通贴纸&#xff08;缩放、Z轴旋转、平移&#xff09; OpenGL环境说明 OpenGL渲染区域使用正交投影换算,正常OpenGL坐标是vertexData,这样的 Matrix.orthoM 进行换算 //顶点坐标&#xff08;原点为显示区域中心店&#xff09;private final float[] vertex…

小米公司研发岗的年终奖。。

小米 好的公司有年终且在年前发放&#xff0c;一般的公司有&#xff08;可能打折的&#xff09;年终且年后分批发放&#xff0c;不好的公司各有操作。 3 月已来&#xff0c;小米的年终也开始热议起来。 最近&#xff0c;一则「传小米年终打折&#xff0c;14薪能保住吗」冲上热搜…

electron + vtkjs加载模型异常,界面显示类似于图片加载失败的图标

electron vtkjs加载模型显示异常&#xff0c;类似于图片加载失败的效果&#xff0c;如上图。 electron开发版本&#xff1a;13。 解决方法&#xff1a;升级electron版本号。 注意&#xff1a;win7最高兼容electron 22版本。

华为机考:HJ3 明明的随机数

华为机考&#xff1a;HJ3 明明的随机数 描述 代码 set&#xff0c;插入即排序&#xff0c;而且没有重复数字 #include<iostream> #include<vector> #include<algorithm> using namespace std;int main(){int n;while(cin >> n){ //首先输入每次调查…

Rabbit算法:轻量高效的加密利器

title: Rabbit算法&#xff1a;轻量高效的加密利器 date: 2024/3/13 18:14:31 updated: 2024/3/13 18:14:31 tags: Rabbit算法流密码高安全性高性能密钥调度加密解密抗攻击性 Rabbit算法起源&#xff1a; Rabbit算法是由Martin Boesgaard和Mette Vesterager提出的一种流密码算…

1960-2020年全球双边迁移数据库(Global Bilateral MigrationDatabase)

1960-2020年全球双边迁移数据库&#xff08;Global Bilateral MigrationDatabase&#xff09; 1、时间&#xff1a;1960-2000年&#xff0c;每10年一次具体为&#xff1a;1960年、1970年、1980年、1990年、2000年 2、来源&#xff1a;世界银行 3、指标&#xff1a;Country O…

这款自动引流软件居然能让你的营销效果翻倍提升!

在数字化时代&#xff0c;营销策略的高效执行对企业来说至关重要。自动引流软件作为现代企业营销工具箱中的一员&#xff0c;其重要性不言而喻。这类软件通过智能化、自动化的方式&#xff0c;将潜在客户吸引到企业的销售渠道中&#xff0c;从而为企业带来可观的收益和品牌曝光…

【算法】一类支持向量机OC-SVM(1)

【算法】一类支持向量机OC-SVM 前言一类支持向量机OC-SVM 概念介绍示例编写数据集创建实现一类支持向量机OC-SVM完整的示例输出 前言 由于之前毕设期间主要的工具就是支持向量机&#xff0c;从基础的回归和分类到后来的优化&#xff0c;在接触到支持向量机还有一类支持向量机的…

能发顶会!GNN结合LLMs的三大创新思路!新SOTA准确率提升10倍

LLMs在处理NLP任务方面表现出色&#xff0c;而GNNs在挖掘和分析复杂关系数据&#xff08;图数据&#xff09;方面展现出其卓越的能力。这种趋势催生了将这两种技术整合的研究兴趣&#xff0c;为解决更多领域的实际问题。GNN结合LLMs也逐渐成为了研究的热点。 GNNLLMs可以发挥二…

使用Docker管理linux容器

文章目录 一、使用docker管理镜像 二、使用docker管理容器 一、使用docker管理镜像 1、安装操作系统&#xff0c;我安装的是centOS 7 &#xff0c;因为centos7有着非常丰富的软件仓库&#xff0c;方便后续安装与docker相关的软件。 2、初始化设置&#xff0c; 关闭防火墙 关闭…