设计模式之访问者模式

阅读建议

嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:

  1. 本篇文章大概5000多字,预计阅读时间长需要5分钟。
  2. 本篇文章的实战性、理论性较强,是一篇质量分数较高的技术干货文章,建议收藏起来,方便时常学习与回顾,温故而知新。
  3. 创作不易,免费的点赞、关注,请走上一走,算是对博主一些鼓励,让我更有动力输出更多的干货内容。

什么是访问者模式

        访问者模式是一种行为设计模式,它允许你在不改变类的结构的情况下增加新的操作。它是通过让对象决定哪些算法可以作用于它所包含的元素,从而增加新的操作类型来实现的。访问者模式是一种非常有用的模式,它可以将数据结构与数据操作分离,增加新的操作类型,而不改变数据结构。

        在访问者模式中,有一个元素接口,它定义了所有元素类的公共方法,即接受访问的方法。然后,每个元素类都实现了这个接口,将自己的特定数据和操作封装起来。接着,有一个访问者接口,它定义了所有访问者的公共方法,即访问元素的方法。每个访问者类都实现了这个接口,并实现了访问元素的特定操作。

        在客户端代码中,你可以创建元素对象和访问者对象,然后使用元素对象的 accept 方法接受访问者的访问。访问者会根据元素对象的类型调用相应的访问方法,从而实现了一种在不改变元素类的情况下增加新的操作的方式。

访问者模式有哪些核心角色

  1. 访问者(Visitor):这是一个抽象类,它定义了一个访问具体元素的接口,并为每个具体元素类对应一个访问操作visit()。
  2. 具体访问者(ConcreteVisitor):实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  3. 抽象元素(Element):声明一个包含接受操作accept()的接口,被接受的访问者对象作为accept()方法的参数。
  4. 具体元素(ConcreteElement):实现抽象元素角色提供的accept()操作,其方法体通常都是visitor.visit(this)。具体元素中可能还包含本身业务逻辑的相关操作。

这些核心角色共同实现了访问者模式,可以在不改变类的结构的情况下增加新的操作,将数据结构与数据操作分离。

首先,我们定义一个“元素”接口:

public interface Element {  void accept(Visitor visitor);  
}

然后,我们创建一些具体的元素类。在这个例子中,我们只有两种元素:ConcreteElementA 和 ConcreteElementB:

public class ConcreteElementA implements Element {  @Override  public void accept(Visitor visitor) {  visitor.visit(this);  }  
}  public class ConcreteElementB implements Element {  @Override  public void accept(Visitor visitor) {  visitor.visit(this);  }  
}

接着,我们定义一个“访问者”接口:

public interface Visitor {  void visit(ConcreteElementA elementA);  void visit(ConcreteElementB elementB);  
}

然后我们创建一些具体的访问者类:在这个例子中,我们只有一种访问者:ConcreteVisitor:

public class ConcreteVisitor implements Visitor {  @Override  public void visit(ConcreteElementA elementA) {  System.out.println("ConcreteVisitor visited ConcreteElementA");  }  @Override  public void visit(ConcreteElementB elementB) {  System.out.println("ConcreteVisitor visited ConcreteElementB");  }  
}

最后,我们在客户端代码中使用访问者模式:

public class Client {  public static void main(String[] args) {  Element elementA = new ConcreteElementA();  Element elementB = new ConcreteElementB();  Visitor visitor = new ConcreteVisitor();  elementA.accept(visitor);  // 输出 "ConcreteVisitor visited ConcreteElementA"  elementB.accept(visitor);  // 输出 "ConcreteVisitor visited ConcreteElementB"  }  
}

访问者模式如何实现

需求描述

        要说三国中最创业最牛的还属在桃园拜把子的刘关张,谁能想一个编草鞋的、一个杀猪的、还有一个在逃犯,居然这么厉害,从零成功打下江山还立国了,当然最后最守住江山确实很可惜;但要说这成功的秘密,还得刘备这当大哥的会画饼,并且关、张这种狠人,还只吃他的饼,你说厉害不。在创业的路上,刘备带着两个迷弟是从一个失败走向另一个失败,但是刘、关、张之间的结构关系是相当的稳定,“我听大哥的!”,“俺也一样!”。

        如果把刘关张套用到访问者模式中,刘备实际上相当于具体的访问者,最稳定的技能就是哭和画饼;关、张就是具体元素,结构非常稳定,就是只听大哥的,如果想指挥他们,就得通过大哥发话才好使。

        下面通过伪代码示例说明一个访问者模式如何实现。

实现方法

  1. DaGe.java:抽象大哥接口,即抽象访问者的角色,定义大哥的抽象能力,供具体的访问者实现,并为每一个具体的元素类调用;
/*** 抽象大哥*/
public interface DaGe {/*** 给关羽画饼* @param zhangFei*/void huabing(GuanYu zhangFei);/*** 给张飞画饼* @param zhangFei*/void huabing(ZhangFei zhangFei);
}
  1. MengJiang.java:抽象猛将接口,定义作为猛将应该具备的能力-服务大哥的命令,所以会接受一个抽象大哥接口作为参数,即抽象元素;
/*** 抽象猛将*/
public interface MengJiang {/*** 接受命令* @param daGe*/void accept(DaGe daGe);
}
  1. GuanYu.java、ZhangFei.java:实现其猛将接口,即具体的元素;
public class GuanYu implements MengJiang {private String name = "云长";public String getName() {return name;}@Overridepublic void accept(DaGe daGe) {daGe.huabing(this);System.out.println(this.name + ":我听大哥的!");}
}
public class ZhangFei implements MengJiang {private String name = "翼德";public String getName() {return name;}@Overridepublic void accept(DaGe daGe) {daGe.huabing(this);System.out.println(this.name + ":俺也一样!");}
}
  1. LiuBei.java:抽象大哥接口的实现类,确定者访问者具体的业务能力,即画饼。
public class LiuBei implements DaGe{public String name="刘备";@Overridepublic void huabing(GuanYu zhangFei) {String msg=this.name+":二弟,努把力,斩落敌将,晚上给你加个鸡腿!";System.out.println(msg);}@Overridepublic void huabing(ZhangFei zhangFei) {String msg=this.name+":三弟,把家产卖了资助我打江山吧,到时候给你娶个嫂嫂!";System.out.println(msg);}
}
  1. Client.java:编写客户端业务;
public class Client {public static void main(String[] args) {DaGe daGe = new LiuBei();MengJiang guanyu = new GuanYu();MengJiang zhangfei = new ZhangFei();guanyu.accept(daGe);zhangfei.accept(daGe);}
}

如何扩展

        后来孔明做了刘备的军师,代替刘备行使大哥发号使令的权利后,一点点开始成功起来。那么增加孔明这个代理大哥了,铁三角的结构依然是不变的,怎么用代码实现呢?

1、实现大哥的接口,代替刘备向关张发号使令

public class KongMing implements DaGe {private String name = "孔明";@Overridepublic void huabing(GuanYu zhangFei) {String msg = this.name + ":" + zhangFei.getName() + ",悄悄埋伏,守好此道,必能擒住曹贼!";System.out.println(msg);}@Overridepublic void huabing(ZhangFei zhangFei) {String msg = this.name + ":" + zhangFei.getName() + ",且勿酗酒鞭打士兵,好好打仗,我给你娶个弟妹!";System.out.println(msg);}
}

2、修改客户端业务

public class Client {public static void main(String[] args) {DaGe daGe = new LiuBei();MengJiang guanyu = new GuanYu();MengJiang zhangfei = new ZhangFei();guanyu.accept(daGe);zhangfei.accept(daGe);System.out.println("--------------");daGe = new KongMing();guanyu.accept(daGe);zhangfei.accept(daGe);}
}

访问者模式适用哪些场景

        了解了什么是访问者模式、有哪些核心角色以及如何实现后,这里再梳理一下访问者模式适用于哪些业务场景:

  1. 数据结构相对稳定,而算法易变的系统。
  2. 需要对不同数据类型进行操作,而不使用分支判断具体类型的场景。
  3. 需要将数据结构与数据操作分离的场景。
  4. 需要对数据结构进行扩展性操作,例如增加新的操作或新的元素类型,而不需要修改原有元素类的场景。

        需要特别注意的是,访问者模式通过将数据结构和操作分离开来,使得数据结构可以保持稳定,而操作可以随意扩展。这种解耦有助于提高系统的灵活性和可复用性。然而,访问者模式也有一些缺点,例如增加新的数据结构困难、违反了依赖倒置原则等,使用时需要根据具体情况权衡利弊。

访问者模式的优点和缺点

优点

  1. 符合单一职责原则,即数据的存储和操作分别由对象结构类和访问者类实现。
  2. 提供了优秀的扩展性和灵活性。可以通过扩展访问者角色,实现对数据集的不同操作。
  3. 元素具体类型并非单一,访问者均可操作。

缺点

  1. 无法增加元素类型。若系统数据结构易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了开闭原则。
  2. 具体元素变得更困难。具体元素增加属性、删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大。
  3. 具体元素对访问者公布了其细节,违反了迪米特法则;
  4. 违背依赖倒置原则。为了达到“区别对待”,访问者依赖的是具体元素类型,而不是抽象。

总结

        综上所述,访问者模式具有优秀的扩展性和灵活性,但也存在一些缺点需要注意。使用时需要根据具体情况权衡利弊。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ubuntu1604的ROS安装

不同版本的Ubuntu都有对应的ROS版本,不要强行安装不对应的版本,否则遇到问题会很难找到解决方法。此教程也只是基于Ubuntu1604和kinetic版本的ROS。 一、基本流程 以下命令仅记录执行顺序,不要无脑复制执行,重在理解 #基本更新…

​软考-高级-系统架构设计师教程(清华第2版)【第2章 计算机系统基础知识-思维导图】​

软考-高级-系统架构设计师教程(清华第2版)【第2章 计算机系统基础知识-思维导图】 课本里章节里所有蓝色字体的思维导图

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法,简单地说就是把相似的数据样本分到一组(簇),聚类的过程,我们并不清楚某一类是什么(通常无标签信息)&#xff0…

AIGC视频生成/编辑技术调研报告

人物AIGC:FaceChain人物写真生成工业级开源项目,欢迎上github体验。 简介: 随着图像生成领域的研究飞速发展,基于diffusion的生成式模型取得效果上的大突破。在图像生成/编辑产品大爆发的今天,视频生成/编辑技术也引起…

HTML的初步学习

HTML HTML 描述网页的骨架, 标签化的语言. HTML 的执行是浏览器的工作,浏览器会解析 html 的内容,根据里面的代码,往页面上放东西,浏览器的工作归根结底,还是以汇编的形式在CPU上执行. 浏览器对于html语法格式的检查没有很严格,即使你写的代码有一些不合规范之处,浏览器也会尽可…

打开ps提示,计算机中丢失d3dcompiler_47.dll怎么解决?

“d3dcompiler_47.dll丢失5个解决办法”。相信很多同事在工作或者娱乐的过程中,都遇到过这个错误提示。那么,究竟什么是d3dcompiler_47.dll文件?为什么会丢失呢?又该如何解决这个问题呢?接下来,我将为大家详…

angular学习笔记

HTML绑定 形式&#xff1a;{{ 变量名 }} {{}}内部可以是 算数运算比较运算逻辑运算三目运算调用函数 {{}}内部不可以是 创建对象&#xff1a;不可以newJSON序列化 属性绑定 形式1&#xff1a;[属性名]“变量名” 形式2&#xff1a;属性名“{{变量名}}” <div [title…

ClickHouse介绍和使用

ClickHouse介绍和使用 1. 简介2. ClickHouse特点3. 数据类型3.1. 整型3.2. 浮点型3.3. Decimal型3.4. 布尔型3.5. 字符串3.6. 枚举类型3.7. 时间类型 4. 表引擎4.1. TinyLog4.2. Memory4.3. MergeTree4.3.1. partition by分区&#xff08;可选&#xff09;4.3.2. primary key 主…

数据分析是什么?

第一章- 数据分析是什么 数据分析是指 根据分析目的&#xff0c;用适当的分析方法及工具&#xff0c;对数据进行分析&#xff0c;提取有价值的信息&#xff0c;形成有效结论的过程。 数据分析的作用 通过观察数据&#xff0c;知道当前发生什么&#xff1f;通过具体的数据拆解…

【论文阅读】Progressive Spatio-Temporal Prototype Matching for Text-Video Retrieval

资料链接 论文链接&#xff1a;https://openaccess.thecvf.com/content/ICCV2023/papers/Li_Progressive_Spatio-Temporal_Prototype_Matching_for_Text-Video_Retrieval_ICCV_2023_paper.pdf 代码链接&#xff1a;https://github.com/imccretrieval/prost 背景与动机 文章发…

代码随想录算法训练营Day 47 || 198.打家劫舍、213.打家劫舍II、337.打家劫舍 III

198.打家劫舍 力扣题目链接(opens new window) 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系…

Qframework 中超级方便的kitres

using QFramework; using System.Collections; using System.Collections.Generic; using UnityEngine;public class TestResKit : MonoBehaviour {ResLoader mResLoader ResLoader.Allocate();private void Awake(){}/// <summary>/// 每一个需要加载资源的单元(脚本,界…

【Unity之UI编程】在Unity中如何打图集,来降低DrowCall

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

pytest 的使用===谨记

发现用例的规则 a) 文件test_.py开头和_test.py结尾 b) Test开头的类中test开头的方法&#xff08;测试类不能带有__init__方法&#xff09; c) 模块中test开头的函数&#xff08;可以不在class中&#xff09; 注意点&#xff1a; pytest是以方法为单位发现用例的&#xff0c;你…

摔杯算法(要求用最少的测试次数找出恰巧会使杯子破碎的楼层。)

题目: 一种杯子&#xff0c;若在第N层被摔破&#xff0c;则在任何比N高的楼层均会破&#xff1b;若在第M层不破&#xff0c;则在任何比M低的楼层均不会破。给你两个这样的杯子&#xff0c;让你在100层高的楼层中测试&#xff0c;要求用最少的测试次数找出恰巧会使杯子破碎的楼层…

vue:实现顶部消息横向滚动通知

前言 最近有个需求&#xff0c;是在系统顶部展示一个横向滚动的消息通知。需求很简单&#xff0c;就是消息内容从右往左一直滚动。 效果如下&#xff1a; 因为我的需求很简单&#xff0c;功能就这样。如果有什么其他需求&#xff0c;可以再继续修改。 代码 使用 <noti…

数据中台之数据分析

效果界面 技术方案 Notebook集成 在您的数据平台上,创建一个能够与Jupyter Notebook通讯的服务。通过Jupyter Notebook的HTTP API与Notebook实例进行交互,执行代码、获取输出等。用户界面 在数据开发/数据分析的代码框右上方,添加一个机器人样式的图标,用户点击后可以调起…

Ubuntu 安装常见问题

1. 安装oh my zsh 搜狗输入法不能用 vim /etc/environmentexport XIM_PROGRAMfcitx export XIMfcitx export GTK_IM_MODULEfcitx export QT_IM_MODULEfcitx export XMODIFIERS“imfcitx” export LANG“zh_CN.UTF-8”配置完后重启&#xff0c;稍等一会&#xff0c;右上角会有个…

Linux--vim

文章目录 Vim的介绍Vim的几种模式命令模式下的基本操作批量化注释Vim的简单配置使用插件 Vim的介绍 Vim是一个强大的文本编辑器&#xff0c;是从vi编辑器发展而来的&#xff0c;在vi编辑器的基础上进行了改进和拓展&#xff0c;具有强大的特性和功能。 Vim是一个自由开源软件&…

城市内涝积水监测,万宾科技内涝预警监测系统

每一个城市的排水体系都是一个复杂的网络系统&#xff0c;需要多个部分配合协调&#xff0c;预防城市排水管网带来安全隐患&#xff0c;也因此才能在一定程度上缓解城市内涝带来的安全问题。在海绵城市建设过程中不仅要解决大部分道路硬化导致的积水无法渗透等问题&#xff0c;…