0%

设计模式系列——访问者模式

一、基本概念

访问者模式属于设计模式中的【行为型设计模式】,它将行为和具体的数据结构进行了分离。在23种设计模式中,它相对比较复杂,难理解。

二、实现

我们先不探究使用访问者模式的原因,简单来看看访问者模式的实现。
其实访问者模式就像它名字所形容的,可以比喻为多个客人对一个包含多个家庭成员的家庭进行访问,到访的客人会一次和家庭的每个成员进行寒暄,所以它的三个组成要素就是:客人家庭、以及家庭成员

以下是行为模式Golang的一个实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package main

import "fmt"

//家庭成员接口
type Member interface {
//定义一个与访问者发生寒暄的方法
Accept(Visitor)
}

//家里的小女儿
type daughter struct{}

//与访问者互相寒暄
func (d *daughter) Accept(v Visitor) {
v.Visit(d)
}

//家里的小儿子
type son struct{}

//与访问者互相寒暄
func (s *son) Accept(v Visitor) {
v.Visit(s)
}

//访问者接口
type Visitor interface {
//和家庭成员寒暄时要进行的对应操作定义在这里
Visit(Member)
}

//爸爸的同学1
type Classmate1 struct{}

//这是一个玩具厂老板,给孩子送玩具
func (c *Classmate1) Visit(member Member) {
switch member.(type) {
case *son:
fmt.Println("送给一个擎天柱")
case *daughter:
fmt.Println("送给一个洋娃娃")
}
}

//爸爸的同学2
type Classmate2 struct{}

//这是一个程序员,给孩子送编程书
func (c *Classmate2) Visit(member Member) {
switch member.(type) {
case *daughter:
fmt.Println("送一本Java大全")
case *son:
fmt.Println("送一本前端宝典")
}
}

//家庭类
type Family struct {
members []Member
}

//添加家庭成员
func (f *Family) Add(m Member) {
f.members = append(f.members, m)
}

//聚会开始
func (f *Family) Party(v Visitor) {
for _, m := range f.members {
m.Accept(v)
}
}


func main() {
//首先定义家庭成员
family := Family{}
family.Add(&son{})
family.Add(&daughter{})

//根据不同的访问者,和家庭成员之间产生不同的行为
//玩具老板给两个孩子送玩具
v1 := &Classmate1{}
family.Party(v1)
//程序员给孩子送编程书籍
v2 := &Classmate2{}
family.Party(v2)
}

这段代码其实是一个小故事,两个访客分别来家里拜访,一个人是玩具厂老板,他看到家里的小女儿,会送一套洋娃娃,看到小儿子,会送一个擎天柱,一个人是程序员,他看到小女儿,会送本前端宝典,看到家里的小儿子,会送本Java大全。

三、为什么需要访问者模式

从上面的故事,我们可以看出,家庭成员具体发生了什么行为是由来拜访的访客所决定,故事里所有行为都是定义在了访客身上(送书、送玩具),而我们的家庭成员本身与行为是解耦开的。
在业务中我们经常会遇到一种情况,同族类不断膨胀的业务操作,例如:我们我们要处理很多文件类型的文件,他们都要执行读取、写入的操作。
对于这种需求,我们通常的做法就是定义一个txt类、pdf类等文件类,他们都实现read和write方法。但是当需要再额外增加几个新的文件操作,例如:文件压缩、文件水印等时,我们会发现了几个问题:

  • 添加一个新功能,需要给所有的文件类都添加这个操作,违背了开闭原则
  • 随着业务操作和数据类的不断耦合,类违背了单一职责原则

而访问模式通过将行为定义到visitor中,并通过family为媒介,实现member成员类与行为的解耦,行为的增加只需要定义一个新的访问者即可。

欢迎关注我的其它发布渠道