【Go-设计模式】访问者模式 详解

shadowLogo

概念:

访问者模式(Visitor Pattern),封装一些作用于 某种数据结构各元素的操作,它可以在 不改变数据结构 的前提下定义作用于这些元素的 新的操作

主要将 数据结构数据操作 分离,解决 数据结构操作耦合性 问题

工作原理
被访问的类 里面加一个 对外提供接待访问者的接口

visit


UML:

uml

  • Visitor
    抽象访问者,定义了 对每个 Element 访问的行为,它的参数就是 被访问的元素,它的 方法个数 理论上与 元素个数 是一样的
    因此,访问者模式要求 元素的类型要稳定,如果经常 添加移除 元素类,必然会导致 频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式
  • ConcreteVisitor
    是一个 具体的访问者,它需要给出对 每一个元素类 访问时所产生的 具体行为
  • Element
    定义一个 accept方法接收一个访问者对象,意义是指 每一个元素 都要 可以被访问者访问
  • ConcreteElement
    具体元素,实现了 accept 方法,接受访问者
  • ObjectStructure
    定义上文 所提到的对象结构,对象结构是一个抽象表述,
    它内部管理了元素集合,并且可以 迭代这些元素 提供 访问者访问入口
    可以提供一个 高层的接口,用来 允许访问者访问元素

示例:

抽象的访问者接口:

type Visitor interface {
	Visit(element Element)
}

具体的访问者类:

type ConcreteVisitor struct {
}

func (this ConcreteVisitor) VisitElement1(element Element) {
	var element1 = element.(*ConcreteElement1)
	fmt.Printf("visit the concrete visitor1,attribute is %s\n", element1.Attri)
}
func (this ConcreteVisitor) VisitElement2(element Element) {
	var element1 = element.(*ConcreteElement2)
	fmt.Printf("visit the concrete visitor1,attribute is %s\n", element1.Attri)
}

func NewConcreteVisitor() ConcreteVisitor {
	return ConcreteVisitor{}
}

抽象的元素功能接口:

type Element interface {
	Accept(Visitor)
}

具体的元素类:

具体的元素类1:

type ConcreteElement1 struct {
	Attri string
}

func (this *ConcreteElement1) Accept(visitor Visitor) {
	visitor.VisitElement1(this)
}

func NewConcreteElement1(desc string) ConcreteElement1 {
	return ConcreteElement1{
		Attri: desc,
	}
}

具体的元素类2:

type ConcreteElement2 struct {
	Attri string
}

func (this *ConcreteElement2) Accept(visitor Visitor) {
	visitor.VisitElement2(this)
}

func NewConcreteElement2(desc string) ConcreteElement2 {
	return ConcreteElement2{
		Attri: desc,
	}
}

对象结构类:

type ObjectStructure struct {
	Elements map[Element]struct{}
}

func (this *ObjectStructure) AddElement(ele Element) {
	this.Elements[ele] = struct{}{}
}

func (this ObjectStructure) RemoveElement(ele Element) {
	delete(this.Elements, ele)
}

func (this ObjectStructure) DoVisit(visitor Visitor) {
	for element := range this.Elements {
		element.Accept(visitor)
	}
}

func NewObjectStructure() ObjectStructure {
	return ObjectStructure{
		Elements: map[Element]struct{}{},
	}
}

测试:

package main

import "DemoProject/design/base23/visitor" // 根据自己的项目路径定制

func main() {
	// visitor
	objectStructure := visitor.NewObjectStructure()

	element1 := visitor.NewConcreteElement1("具体的元素实例1")
	objectStructure.AddElement(&element1)
	element2 := visitor.NewConcreteElement2("具体的元素实例2")
	objectStructure.AddElement(&element2)

	concreteVisitor := visitor.NewConcreteVisitor()
	objectStructure.DoVisit(concreteVisitor)
}

注意事项:

优点:

  • 符合 单一职责原则
    让程序具有 优秀的 扩展性灵活性
  • 使得 数据结构作用于结构上的操作 解耦,使得 操作集合 可以 独立变化
  • 可以对 功能 进行 统一
    可以做 报表UI拦截器过滤器,适用于 数据结构相对稳定的系统
  • 如果需要 增加新的访问者增加实现类 ConcreteVisitor 就可以 快速扩展
  • 如果一个系统有 比较稳定的数据结构,又有 经常变化的功能需求,那么访问者模式就是比较合适

缺点:

  • 具体元素对访问者 公布细节
    也就是说 访问者 关注了 其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难

  • 违背了 依赖倒转原则
    访问者依赖的是 具体元素,而不是 抽象元素

应用场景:

需要对一个 对象结构 中的对象进行很多 不同操作(这些操作 彼此没有关联),
同时需要 避免 让这些操作 "污染"这些对象的类,可以选用 访问者模式 解决

posted @ 2021-12-17 16:32  在下右转,有何贵干  阅读(136)  评论(0)    收藏  举报