Go设计模式(24)-访问者模式

简介: 访问者模式理解比较困难。可以认为对象开了一扇门,用来接收访问者,然后访问者便可在对象内部操作对象。简单来说,对象对访问者进行了授权。这样做能够实现对象和操作的解耦,职责更加单一。对象只管理自身,操作功能安置在访问者中。

访问者模式理解比较困难。可以认为对象开了一扇门,用来接收访问者,然后访问者便可在对象内部操作对象。简单来说,对象对访问者进行了授权。这样做能够实现对象和操作的解耦,职责更加单一。对象只管理自身,操作功能安置在访问者中。

UML类图位置:https://www.processon.com/view/link/60d29bf3e401fd49502afd25

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/24vistor.go

1.定义

1.1 访问者模式

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

UML:

图片

1.2分析

看完访问者模式定义和UML,可能大家会想,这说的是人话吗?一开始我也是这么想的。但是把定义和UML拆分后,就容易理解了。

定义分析

先看定义:表示一个作用于某对象结构中的各元素的操作。

对象结构中的各元素:就是指一个类和类里的各种成员变量,对应UML中的Element。

操作:就是指访问者,访问者有操作元素的能力。

所以这句话可以解释为:访问者模式,就是访问者可以操作类里的元素。

再看定义:它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

这句话主要讲访问者模式的优点:类在不做任何改动的情况下,能够增加新的操作/解析方式。如元素类是pdf资源文件类,以前只支持抽取文本内容操作,现在增加压缩、提取文件元信息操作,元素类无需感知。

UML分析

定义没有解释访问者模式是如何实现的,这时候我们可以看UML。

首先我们看Element,这个是元素类,属于被操作的对象。元素类有成员函数Accept,用于接收访问者。关于Element想提两点:

  1. 成员函数Accept中的入参vistor是接口或者是父类,不是子类
  2. Element可以有一个operator函数,ConcreteElementA和ConcreteElementB可实现该函数。

再来看Vistor,有两个成员函数,入参分别对应ConcreteElementA、ConcreteElementB,即Vistor提供了同一种功能,能够操作不同的Element。

通过UML分析,我们可以看出,Element和Vistor是你中有我,我中有你的关系。

2.应用场景

虽然分析完了,可能有很多同学会比较懵,这么麻烦的设计模式我用了干嘛!实际情况是,我也没用过访问者模式。但有些场景,访问者模式还是有用的。

设计模式还是为了解耦,实现高内聚、低耦合。

假设有三种文件类型,pdf、word、txt,需要对这三种文件做内容提取、压缩、获取文件元信息操作,我们应该如何设计。

我们肯定需要创建pdf、word、txt三个类,实现文件的读取。

然后我们实现内容提取、压缩、获取文件元信息三个类,每个类有三个函数,用来处理不同类型的文件。

现在已经将所有文件读取完毕,需要对文件分别进行内容提取、压缩、获取文件元信息。

我们可以这么实现:

func test() {
   fileList := make([]int, 10)
  
   fmt.Println("------提取文件")
   for _, f := range fileList {
      if "f是pdf" == "true" {
         fmt.Println("调用pdf提取接口")
      }else "f是txt" == "true" {
         fmt.Println("调用txt提取接口")
      }
   }
   fmt.Println("------压缩文件")
   for _, f := range fileList {
      if "f是pdf" == "true" {
         fmt.Println("调用pdf压缩接口")
      }else "f是txt" == "true" {
         fmt.Println("调用txt压缩接口")
      }
   }
}

如果这样实现,当增加新文件类型或者新功能时,都要修改一堆if-else,不但不优雅,而且极易出问题。这时候就可以使用访问者模式。

3.代码实现

package main

import "fmt"

/**
 * @Author: Jason Pang
 * @Description: 读文件接口,用于获取到文件
 */
type ReadFile interface {
   Read(fileName string)
   Accept(v VistorReadFile)
}

/**
 * @Author: Jason Pang
 * @Description: 读pdf文件类
 */
type ReadPdfFile struct {
}

/**
 * @Author: Jason Pang
 * @Description: 读取文件
 * @receiver p
 * @param fileName
 */
func (p *ReadPdfFile) Read(fileName string) {
   fmt.Println("读取pdf文件" + fileName)
}

/**
 * @Author: Jason Pang
 * @Description: 接受访问者类
 * @receiver p
 * @param v
 */
func (p *ReadPdfFile) Accept(v VistorReadFile) {
   v.VistorPdfFile(p)
}

/**
 * @Author: Jason Pang
 * @Description: 读取txt文件类
 */
type ReadTxtFile struct {
}

/**
 * @Author: Jason Pang
 * @Description: 读取文件
 * @receiver t
 * @param fileName
 */
func (t *ReadTxtFile) Read(fileName string) {
   fmt.Println("读取txt文件" + fileName)
}

/**
 * @Author: Jason Pang
 * @Description: 接受访问者类
 * @receiver p
 * @param v
 */
func (t *ReadTxtFile) Accept(v VistorReadFile) {
   v.VistorTxtFile(t)
}

/**
 * @Author: Jason Pang
 * @Description: 访问者,包含对pdf和txt的操作
 */
type VistorReadFile interface {
   VistorPdfFile(p *ReadPdfFile)
   VistorTxtFile(t *ReadTxtFile)
}

/**
 * @Author: Jason Pang
 * @Description: 提取文件类
 */
type ExactFile struct {
}

/**
 * @Author: Jason Pang
 * @Description: 提取pdf文件
 * @receiver e
 * @param p
 */
func (e *ExactFile) VistorPdfFile(p *ReadPdfFile) {
   fmt.Println("提取pdf文件内容")
}

/**
 * @Author: Jason Pang
 * @Description: 提取txt文件
 * @receiver e
 * @param p
 */
func (e *ExactFile) VistorTxtFile(p *ReadTxtFile) {
   fmt.Println("提取txt文件内容")
}

/**
 * @Author: Jason Pang
 * @Description: 压缩文件类
 */
type CompressionFile struct {
}

/**
 * @Author: Jason Pang
 * @Description: 压缩pdf文件
 * @receiver c
 * @param p
 */
func (c *CompressionFile) VistorPdfFile(p *ReadPdfFile) {
   fmt.Println("压缩pdf文件内容")
}

/**
 * @Author: Jason Pang
 * @Description: 压缩txt文件
 * @receiver c
 * @param p
 */
func (c *CompressionFile) VistorTxtFile(p *ReadTxtFile) {
   fmt.Println("压缩txt文件内容")
}

func main() {
   filesList := []ReadFile{
      &ReadPdfFile{},
      &ReadTxtFile{},
      &ReadPdfFile{},
      &ReadTxtFile{},
   }
   //提取文件
   fmt.Println("--------------------------提取文件")
   extract := ExactFile{}
   for _, f := range filesList {
      f.Accept(&extract)
   }
   //压缩文件
   fmt.Println("--------------------------压缩文件")
   compress := CompressionFile{}
   for _, f := range filesList {
      f.Accept(&compress)
   }
}

输出:

➜ myproject go run main.go

--------------------------提取文件

提取pdf文件内容

提取txt文件内容

提取pdf文件内容

提取txt文件内容

--------------------------压缩文件

压缩pdf文件内容

压缩txt文件内容

压缩pdf文件内容

压缩txt文件内容

这种写法,如果增加新的文件类型,main中代码无需改动,只需要vistor添加新的实现即可。如果增加新的功能,文件类也无需感知。

总结

访问者模式实现了对象和操作的解耦。可以认为访问者模式有两个维度,一是对象和操作解耦,这个比较容易理解,也符合单一职责原则。二是对象给操作开个大门,这个是否需要主要看业务的复杂度。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

  1. 设计模式
  2. 招聘
  3. 思考
  4. 存储
  5. 算法系列
  6. 读书笔记
  7. 小工具
  8. 架构
  9. 网络
  10. Go语言
相关文章
|
3月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
43 3
|
8月前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
|
4月前
|
设计模式 缓存 算法
Java设计模式-访问者模式(22)
Java设计模式-访问者模式(22)
|
5月前
|
设计模式 Go
go 设计模式之观察者模式
go 设计模式之观察者模式
|
6月前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
80 8
|
5月前
|
设计模式 算法 Go
|
8月前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
54 2
|
8月前
|
设计模式 Go
[设计模式 Go实现] 结构型~享元模式
[设计模式 Go实现] 结构型~享元模式
|
8月前
|
设计模式 Go API
[设计模式 Go实现] 结构型~外观模式
[设计模式 Go实现] 结构型~外观模式
|
8月前
|
设计模式 Go
[设计模式 Go实现] 结构型~组合模式
[设计模式 Go实现] 结构型~组合模式

热门文章

最新文章

  • 1
    设计模式转型:从传统同步到Python协程异步编程的实践与思考
    64
  • 2
    C++一分钟之-设计模式:工厂模式与抽象工厂
    54
  • 3
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    60
  • 4
    C++一分钟之-C++中的设计模式:单例模式
    79
  • 5
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    47
  • 6
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    80
  • 7
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    70
  • 8
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    54
  • 9
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    63
  • 10
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    137