设计模式之访问者模式

阅读建议

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

  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.mzph.cn/news/134925.shtml

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

相关文章

利用QT画图像的直方图

1.什么是直方图 直方图是一种图形化展示数据频率分布的方式。它将样本数据分成一系列相邻的区间,统计每个区间内数据所占比例或数量,并用矩形条形图表现出来。直方图可以反映样本数据的分布情况,例如它们的集中趋势、对称性和离散程度等。 …

Java2 - 数据结构

5 数据类型 5.1 整数类型 在Java中,数据类型用于定义变量或表达式可以存储的数据的类型。Java的数据类型可分为两大类:基本数据类型和引用数据类型。 byte,字节 【1字节】表示范围:-128 ~ 127 即:-2^7 ~ 2^7 -1 sho…

基于Kinect 动捕XR直播解决方案 - 硬件篇

Kinect-V2 硬件设备 一、Kinect介绍 1、Kinect for Windows 的开发配置 Kinect V2 操作系统:Windows 10(必须) Windows Surface Windows Surface 2 开发环境:Visual Studio 2017 .NET Framework 4.5 (.NET Framework 4.5) 硬…

VEX —— Quaternion|Euler Angle

目录 一,四元数相关概念 四元数 欧拉角 常用四元数相关函数 相互转换 二,案例 案例:沿面中心翻转 案例:路径导弹 案例:RBD刚体还原过渡 一,四元数相关概念 四元数 在vex内四元数为(&am…

BigDecimal 类型的累加操作

BigDecimal 累加操作 .add操作

C语言:深入浅出qsort方法,编写自己的qsort完成冒泡排序

目录 什么是qsort? 函数原型 比较函数 compar 排序整型数组 排序结构体数组 根据成员字符排序 strcmp函数 根据成员整型排序 自定义qsort实现冒泡排序 qsort的实现原理 具体步骤 快速排序示例代码: 什么是qsort? qsort是 C …

geoserver发布同一字段的多值渲染

Geoserver之同一字段的多值渲染 有时候我们需要对一个shp的某一字段值中的不同值进行区分展示,但是一般的渲染都是按照统一图层展示的,因此为了更好的效果,我们选择使用uDig等工具处理。 文章目录 Geoserver之同一字段的多值渲染前言一共是分…

休眠和睡眠有哪些区别?如何让电脑一键休眠?

电脑中有休眠和睡眠,那么它们有什么区别呢?下面我们就通过本文来了解一下。 休眠和睡眠的区别 电脑在睡眠状态时,会切断内存之外的设备电源,电脑会进入睡眠状态,当再次唤醒电脑后,不会影响睡眠前保存好的工…

机器学习练习1

线性回归 数据集模型,第一列代表人口,第二列代表利润 此处的线性回归 与 常规的 y wx b 不同的是 将b换成了w的一部分 故需要在数据集x前面加个1, 求出b . 可以简化计算,只需要一个乘法就可以

网络安全入门必学内容

网络安全入门 必/学/内/容/ 随着时代的发展,经济、社会、生产、生活越来越依赖网络。而随着万物互联的物联网技术的兴起,线上线下已经打通,虚拟世界和现实世界的边界正变得模糊。这使得来自网络空间的攻击能够穿透虚拟世界的边界&#xff0…

vscode中 vue3+ts 项目的提示失效,volar插件失效问题解决方案

文章目录 前情提要bug回顾解决方案最后 前情提要 说起来很耻辱,从mac环境换到window环境,vscode的配置都是云端更新过来的,应该是一切正常才对,奇怪的是我的项目环境出现问题了,关于组件的ts和追踪都没有效果&#xff…

vscode设置pycharm中的项目路径和debug方法

真大佬在这 真大佬在这 必须给大佬star 命令行运行: export PYTHONPATH:pwd:/home/bennie/bennie/bennie_project/AI_Lab python main.py 当关闭此命令行时,临时路径会清除,可以将上述export的整条语句,加入~/.bashrc中 该命令中…

重磅发布 OpenAI 推出用户自定义版 ChatGPT

文章目录 重磅发布 OpenAI 推出用户自定义版 ChatGPT个人简介 重磅发布 OpenAI 推出用户自定义版 ChatGPT OpenAI 首届开发者大会 (OpenAI DevDay) 于北京时间 11 月 7 日凌晨 02:00 开始,大会上宣布了一系列平台更新。其中一个重要更新是用户可以创建他们自己的自定…

从零开始的C++(十四)

继承: 作用:减少重复代码,简化程序。 用法: class b:public a {//...b中成员 } 在如上代码中,b类以public的方式继承了a类。规定a类是父类、基类,b类是子类、派生类。 关于继承方式&#xf…

Qt::WindowFlags

Qt::WindowFlags 文章目录 Qt::WindowFlags摘要窗口&部件Qt::WindowFlags&WindowType窗口类型窗口提示 关键字: Qt、 Qt::WindowFlags、 Qt::WindowType、 关键字4、 关键字5 摘要 今天在公司解决自己的Bugs的时候,发现一个以前可以用的功…

在Kotlin中设置User-Agent以模拟搜索引擎爬虫

前言 随着双十一电商活动的临近,电商平台成为了狂欢的中心。对于商家和消费者来说,了解市场趋势和竞争对手的信息至关重要。在这个数字时代,爬虫技术成为了获取电商数据的有力工具之一。本文将以亚马逊为例,介绍如何使用Kotlin编…

软件测试面试题【2023最新合集】

收集了各大公司的面试经验,现整理出来,希望能给正在找工作的志同道合的小伙伴一些指引,本文会持续更新的哦。 1、 CPU 和 GPU的区别 一个是通用计算,一个是专用计算。 CPU主要负责操作系统和应用程序,GPU主要负责跟…

【BUG解决】服务器没报警但是应用接口崩了....

最近遇到一个突发问题:服务器没报警但是应用接口崩了… 为其他业务系统提供一个接口,平时好好的,突然就嚷嚷反馈说访问不了了,吓得我赶紧跳起来! 正常情况下在系统崩溃前,我会收到很多系统报警&#xff0…

【AI编程】ai编程插件汇总iFlyCode、codegeex

1、iFlyCode 开发公司:讯飞 支持IDE: VS Code、IntelliJ IDEA、CLion、PyCharm、WebStorm 支持语言: Python、JavaScript、C、Java 下载地址:https://iflycode.xfyun.cn/ iFlyCode 快捷键列表:  Tab 采纳建议  Esc 拒绝建议  Alt\ 主动…

CSDN每日一题学习训练——Java版(对给定的两个日期之间的日期进行遍历、子集 II、填充每个节点的下一个右侧节点指针)

版本说明 当前版本号[20231107]。 版本修改说明20231107初版 目录 文章目录 版本说明目录对给定的两个日期之间的日期进行遍历题目解题思路代码思路参考代码 子集 II题目解题思路代码思路参考代码 填充每个节点的下一个右侧节点指针题目解题思路代码思路参考代码 对给定的两…