访问者模式

定义

访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变元素类的前提下定义作用于这些元素的新操作。

使用访问者模式,元素的执行算法可以随着访问者改变而改变。主要意图是将数据结构与数据操作分离。

不过作为比较难理解的设计模式之一,因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,访问者模式是不建议使用的。

优点

1、开闭原则。 你可以引入在不同类对象上执行的新行为, 且无需对这些类做出修改。

2、单一职责原则。 可将同一行为的不同版本移到同一个类中。

3、灵活性更好。

缺点

1、具体元素变更比较困难。每次在元素层次结构中添加或移除一个类时,都要更新所有的访问者。

2、比较难理解,应用它会导致代码的可读性、可维护性变差。

适用范围

1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。

代码实现

代码实现:

type Visitor interface {
VisitConcreteElementA(cea *ConcreteElementA)
VisitConcreteElementB(ceb *ConcreteElementB)
} type ConcreteVisitor1 struct {
} func (cea *ConcreteVisitor1) VisitConcreteElementA(concreteElementA *ConcreteElementA) {
fmt.Println("concreteVisitor1 visitConcreteElementA")
} func (*ConcreteVisitor1) VisitConcreteElementB(concreteElementB *ConcreteElementB) {
fmt.Println("concreteVisitor1 visitConcreteElementB")
} type ConcreteVisitor2 struct {
} func (*ConcreteVisitor2) VisitConcreteElementA(concreteElementA *ConcreteElementA) {
fmt.Println("concreteVisitor2 visitConcreteElementA")
} func (*ConcreteVisitor2) VisitConcreteElementB(concreteElementB *ConcreteElementB) {
fmt.Println("concreteVisitor2 visitConcreteElementB")
} type Element interface {
Accept(visitor Visitor)
} type ConcreteElementA struct {
} func (cea *ConcreteElementA) Accept(visitor Visitor) {
visitor.VisitConcreteElementA(cea)
} type ConcreteElementB struct {
} func (ceb *ConcreteElementB) Accept(visitor Visitor) {
visitor.VisitConcreteElementB(ceb)
}

测试代码:

func TestVisitor(t *testing.T) {
var elements []Element
elements = append(elements, &ConcreteElementA{})
elements = append(elements, &ConcreteElementB{}) for _, item := range elements {
cv1 := &ConcreteVisitor1{}
cv2 := &ConcreteVisitor2{}
item.Accept(cv1)
item.Accept(cv2)
}
}

结构图:

什么是 Double Dispatch

什么是分派?

分派即 Dispatch,在面向对象编程语言中,我们可以把方法调用理解为一种消息传递(Dispatch)。一个对象调用另一个对象的方法,相当于给被调用对象发送一个消息,这个消息包括对象名、方法名、方法参数等信息。

什么是单分派?

单分派,即执行哪个对象的方法,根据对象的运行时类型决定;执行对象的哪个方法,根据方法参数的编译时类型决定。

什么是双分派?

双分派,即执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的运行时的类型来决定。

具体到编程语言的语法机制,Single Dispatch 和 Double Dispatch 跟多态和函数重载直接相关。所以 go 是不支持双分派的。

当前主流的面向对象编程语言(比如,Java、C++、C#)都只支持Single Dispatch,不支持Double Dispatch。

使用 java 举栗子更容易理解:

import java.util.ArrayList;
import java.util.List; abstract class ResourceFile {
protected String filePath; public ResourceFile(String filePath) {
this.filePath = filePath;
}
} class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
} class PPTFile extends ResourceFile {
public PPTFile(String filePath) {
super(filePath);
}
} //...PPTFile、WordFile代码省略...
class Extractor { public void extract2txt(PdfFile pdfFile) {
System.out.println("Extract PDF.");
} public void extract2txt(PPTFile ppTFile) {
System.out.println("Extract PPT.");
}
} public class Test {
public static void main(String[] args) {
Extractor extractor = new Extractor();
List<ResourceFile> resourceFiles = listAllResourceFiles(); for (ResourceFile resourceFile : resourceFiles) {
extractor.extract2txt(resourceFile);
}
} private static List<ResourceFile> listAllResourceFiles() {
List<ResourceFile> resourceFiles = new ArrayList<>();
//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)
resourceFiles.add(new PPTFile("a.ppt"));
resourceFiles.add(new PdfFile("a.pdf")); return resourceFiles;
}
}

比如这段代码,就会在extractor.extract2txt(resourceFile);,代码会在运行时,根据参数(resourceFile)的实际类型(PdfFile、PPTFile、WordFile),来决定使用extract2txt的三个重载函数中的哪一个。那下面的代码实现就能正常运行了。

报错信息

java: 对于extract2txt(ResourceFile), 找不到合适的方法
方法 Extractor.extract2txt(PdfFile)不适用
(参数不匹配; ResourceFile无法转换为PdfFile)
方法 Extractor.extract2txt(PPTFile)不适用
(参数不匹配; ResourceFile无法转换为PPTFile)

参考

【文中代码】https://github.com/boilingfrog/design-pattern-learning/tree/master/访问者模式

【大话设计模式】https://book.douban.com/subject/2334288/

【极客时间】https://time.geekbang.org/column/intro/100039001

【双分派-访问者模式的前世今生】https://www.codenong.com/cs110749395/

【访问者模式】https://boilingfrog.github.io/2021/11/25/使用go实现访问者模式/

设计模式学习-使用go实现访问者模式的更多相关文章

  1. C#设计模式学习笔记:(21)访问者模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第九个模式--访 ...

  2. Java 设计模式系列(二三)访问者模式(Vistor)

    Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...

  3. 设计模式之第20章-访问者模式(Java实现)

    设计模式之第20章-访问者模式(Java实现) “嘿,你脸好红啊.”“精神焕发.”“怎么又黄了?”“怕冷,涂的,涂的,蜡.”“身上还有酒味,露馅了吧,原来是喝酒喝的啊.”“嘿嘿,让,让你发现了,今天来 ...

  4. C#设计模式之二十二访问者模式(Visitor Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第九个模式,该模式是[访问者模式],英文名称是:Visitor Pattern.如果按老规矩,先从名称上来看看这个模式,我根本不能获 ...

  5. 【设计模式】行为型09访问者模式(Visitor Pattern)

    学习地址:https://blog.csdn.net/u012124438/article/details/70537203(参考了很多博客,只有这个讲明白了核心点) 访问者模式(Visitor  P ...

  6. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  7. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  8. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  9. C#设计模式学习笔记:(23)解释器模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第十一个模式-- ...

随机推荐

  1. 《手把手教你》系列技巧篇(三十二)-java+ selenium自动化测试-select 下拉框(详解教程)

    1.简介 在实际自动化测试过程中,我们也避免不了会遇到下拉选择的测试,因此宏哥在这里直接分享和介绍一下,希望小伙伴或者童鞋们在以后工作中遇到可以有所帮助. 2.select 下拉框 2.1Select ...

  2. 异构智联Wi-Fi+蓝牙模组,连接快、准、稳!

    下班回家打开门,电灯.电视.空调.音响.电动窗帘.扫地机器人--一呼百应,有序开工,原本冰冷的房子立刻变成了温暖港湾.可以说,舒适便捷的智能设备已经完全融入了我们的生活中. 从单一场景.单一设备,到现 ...

  3. python解释器和Pycharm编辑器安装使用完整详细教程

    一.官网下载或软件管家公众号下载 二.安装Python解释器 1.选择自定义安装并添加到环境变量 2.检验Python是否安装成功 三.安装pycharm编辑器 1.点击安装,修改安装路径,建议安装C ...

  4. 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef

    基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...

  5. 第五课第四周笔记2:Self-Attention 自注意力

    Self-Attention 自注意力 让我们跳进去谈谈transformer的self-attention机制.如果您能了解本视频背后的主要思想,您就会了解变压器网络工作背后最重要的核心思想. 让我 ...

  6. PinPoint单节点部署及客户端配置方法

    在一次做项目中,需要涉及全链路压测,为了更好定位链路中某一节点可能会出现的问题,在繁忙之余,快速部署及应用了该链路工具,分享给大家~ 话不多说,开始部署~ 一.环境配置1.1 获取需要的依赖包进入ho ...

  7. 搬运2:早期写的探究printf

    目录: 1. 关于printf格式化输出 2. printf的一般形式 3. 转换说明 4. 格式化输出的意义 5. 转换说明修饰符 6. 修饰符中的标记 7. printf的返回值 ps:共3250 ...

  8. cf12D Ball(MAP,排序,贪心思想)

    题意: N位女士一起聚在一个舞厅.每位女士有三个特征值B,I,R.分别代表美貌,智慧,富有. 对于一位女士而言,如果存在一个女士的B,I,R都分别大于她自己的B,I,R.则她自己会自杀. 统计总共有多 ...

  9. hdu 1158 Employment Planning(DP)

    题意: 有一个工程需要N个月才能完成.(n<=12) 给出雇佣一个工人的费用.每个工人每个月的工资.解雇一个工人的费用. 然后给出N个月所需的最少工人人数. 问完成这个项目最少需要花多少钱. 思 ...

  10. 面试官:JavaScript如何实现数组拍平(扁平化)方法?

    面试官:JavaScript如何实现数组拍平(扁平化)方法? 1 什么叫数组拍平? 概念很简单,意思是将一个"多维"数组降维,比如: // 原数组是一个"三维" ...