Go设计模式之访问者模式讲解和代码示例

 更新时间:2023年08月25日 08:27:26   作者:demo007x  
访问者是一种行为设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为,本文将通过代码示例给大家详细的介绍一下Go设计模式之访问者模式,需要的朋友可以参考下

Go 访问者模式讲解和代码示例

访问者是一种行为设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。

阅读我们的文章访问者和双分派以了解为什么不能通过方法重载来简单地替换访问者。

概念示例

访问者模式允许你在结构体中添加行为, 而又不会对结构体造成实际变更。 假设你是一个代码库的维护者, 代码库中包含不同的形状结构体, 如:

  • 方形
  • 圆形
  • 三角形

上述每个形状结构体都实现了通用形状接口。

在公司员工开始使用你维护的代码库时, 你就会被各种功能请求给淹没。 让我们来看看其中比较简单的请求: 有个团队请求你在形状结构体中添加 get­Area获取面积行为。

解决这一问题的办法有很多。

第一个选项便是将 get­Area方法直接添加至形状接口, 然后在各个形状结构体中进行实现。 这似乎是比较好的解决方案, 但其代价也比较高。 作为代码库的管理员, 相信你也不想在每次有人要求添加另外一种行为时就去冒着风险改动自己的宝贝代码。 不过, 你也一定想让其他团队的人还是用一用自己的代码库。

第二个选项是请求功能的团队自行实现行为。 然而这并不总是可行, 因为行为可能会依赖于私有代码。

第三个方法就是使用访问者模式来解决上述问题。 首先定义一个如下访问者接口:

type visitor interface {
    visitForSquare(square)
    visitForCircle(circle)
    visitForTriangle(triangle)
}

我们可以使用 visit­For­Square­(square)visit­For­Circle­(circle)以及 visit­For­Triangle­(triangle)函数来为方形、 圆形以及三角形添加相应的功能。

你可能在想, 为什么我们不再访问者接口里面使用单一的 visit­(shape)方法呢? 这是因为 Go 语言不支持方法重载, 所以你无法以相同名称、 不同参数的方式来使用方法。

好了, 第二项重要的工作是将 accept接受方法添加至形状接口中。

func accept(v visitor)

所有形状结构体都需要定义此方法, 类似于:

func (obj *square) accept(v visitor){
    v.visitForSquare(obj)
}

等等, 我刚才是不是提到过, 我们并不想修改现有的形状结构体? 很不幸, 在使用访问者模式时, 我们必须要修改形状结构体。 但这样的修改只需要进行一次。

如果添加任何其他行为, 比如 get­Num­Sides获取边数和 get­Middle­Coordinates获取中点坐标 , 我们将使用相同的 accept­(v visitor)函数, 而无需对形状结构体进行进一步的修改。

最后, 形状结构体只需要修改一次, 并且所有未来针对不同行为的请求都可以使用相同的 accept 函数来进行处理。 如果团队成员请求 get­Area行为, 我们只需简单地定义访问者接口的具体实现, 并在其中编写面积的计算逻辑即可。

shape.go: 元件

package main
// 形状结构体
type Shape interface {
	getType() string
	accept(Visitor)
}

square.go: 具体元件

package main
type Square struct {
	side int
}
func (s *Square) accept(v Visitor) {
	v.visitForSquare(s)
}
func (s *Square) getType() string {
	return "Square"
}

circle.go: 具体元件

package main
type Circle struct {
	radius int
}
func (c *Circle) accept(v Visitor) {
	v.visitForCircle(c)
}
func (c *Circle) getType() string {
	return "Circle"
}

rectangle.go: 具体元件

package main
type Rectangle struct {
	l int
	b int
}
func (t *Rectangle) accept(v Visitor) {
	v.visitForrectangle(t)
}
func (t *Rectangle) getType() string {
	return "rectangle"
}

visitor.go: 访问者

package main
type Visitor interface {
	visitForSquare(*Square)
	visitForCircle(*Circle)
	visitForrectangle(*Rectangle)
}

areaCalculator.go: 具体访问者

package main
import "fmt"
type AreaCalculator struct {
	area int
}
func (a *AreaCalculator) visitForSquare(s *Square) {
	fmt.Println("calculating area for square")
}
func (a *AreaCalculator) visitForCircle(s *Circle) {
	fmt.Println("Calculating area for circle")
}
func (a *AreaCalculator) visitForrectangle(s *Rectangle) {
	fmt.Println("Calculating area for rectangle")
}

middleCoordinates.go: 具体访问者

package main
import "fmt"
type MiddleCoordinates struct {
	x int
	y int
}
func (a *MiddleCoordinates) visitForSquare(s *Square) {
	fmt.Println("Calculating middle point coordinates for square")
}
func (a *MiddleCoordinates) visitForCircle(c *Circle) {
	fmt.Println("Calculating middle point coordinates for circle")
}
func (a *MiddleCoordinates) visitForrectangle(t *Rectangle) {
	fmt.Println("Calculating middle point coordinates for rectangle")
}

main.go: 客户端代码

package main
import "fmt"
func main() {
	square := &Square{side: 2}
	circle := &Circle{radius: 3}
	rectangle := &Rectangle{l: 2, b: 3}
	areaCalculator := &AreaCalculator{}
	square.accept(areaCalculator)
	circle.accept(areaCalculator)
	rectangle.accept(areaCalculator)
	fmt.Println()
	middleCoordinates := &MiddleCoordinates{}
	square.accept(middleCoordinates)
	circle.accept(middleCoordinates)
	rectangle.accept(middleCoordinates)
}

output.txt: 执行结果

calculating area for square
Calculating area for circle
Calculating area for rectangle

Calculating middle point coordinates for square
Calculating middle point coordinates for circle

到此这篇关于Go设计模式之访问者模式讲解和代码示例的文章就介绍到这了,更多相关Go访问者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go使用协程批量获取数据加快接口返回速度

    Go使用协程批量获取数据加快接口返回速度

    这篇文章主要介绍了Go使用协程批量获取数据加快接口返回速度,使用Go语言后,可以并发获取,极大提升效率,需要的朋友可以参考下
    2023-02-02
  • go语言计算两个时间的时间差方法

    go语言计算两个时间的时间差方法

    这篇文章主要介绍了go语言计算两个时间的时间差方法,涉及Python操作时间的技巧,需要的朋友可以参考下
    2015-03-03
  • 深入讲解Go语言中函数new与make的使用和区别

    深入讲解Go语言中函数new与make的使用和区别

    大家都知道Go语言中的函数new与函数make一直是新手比较容易混淆的东西,看着相似,但其实不同,不过解释两者之间的不同也非常容易,下面这篇文章主要给大家介绍了关于Go语言中函数new与make区别的相关资料,需要的朋友可以参考下。
    2017-10-10
  • golang中使用mongo的方法介绍

    golang中使用mongo的方法介绍

    这篇文章主要给大家介绍了关于golang中使用mongo的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • Go语言如何通过通信共享内存

    Go语言如何通过通信共享内存

    这篇文章主要为大家介绍了Go语言如何通过通信共享内存实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • go test 命令示例详解

    go test 命令示例详解

    go test是Go用来执行测试函数(test function)、基准函数(benchmark function)和示例函数(example function)的命令,这篇文章主要介绍了go test 命令,需要的朋友可以参考下
    2023-11-11
  • golang logrus日志框架实例详解

    golang logrus日志框架实例详解

    logrus是一个可插拔的、结构化的日志框架,这篇文章主要介绍了golang logrus日志框架实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • go内存缓存BigCache使用入门详解

    go内存缓存BigCache使用入门详解

    这篇文章主要为大家介绍了go内存缓存BigCache使用入门详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • 基于context.Context的Golang loader缓存请求放大问题解决

    基于context.Context的Golang loader缓存请求放大问题解决

    这篇文章主要为大家介绍了基于context.Context的Golang loader缓存请求放大解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Go语言中sync.Mutex的使用方法

    Go语言中sync.Mutex的使用方法

    本文主要介绍了golang中sync.Mutex的实现方法,mutex主要有两个 method:Lock()和Unlock(),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03

最新评论