访问者模式通过分离数据结构与操作,在Go中实现灵活扩展。定义Element接口及Accept方法,让TextElement和ImageElement接受Visitor访问;Visitor接口包含VisitText和VisitImage方法,不同操作如HTML导出、字数统计、敏感词检查分别实现该接口;新增操作无需修改元素类,只需添加新访问者;使用时遍历元素调用Accept传入具体访问者,执行对应逻辑;适用于文档处理、AST解析等场景,但需为每种元素实现所有访问者方法,存在样板代码。

在Go语言中实现访问者模式,主要是为了解决在不修改原有数据结构的前提下,动态添加新的操作逻辑。这种设计模式特别适合数据结构相对稳定,但需要频繁扩展操作的场景。
访问者模式核心思想
将数据结构与作用于结构上的操作分离,使得操作可以独立变化。通过定义一个访问者接口,让不同的操作实现该接口,从而实现对同一组对象的不同处理逻辑。
以一个简单的文档处理系统为例,假设我们有文本元素和图片元素,未来可能需要添加导出为HTML、统计字数、校验内容等多种操作。
定义元素接口和具体元素
元素接口需要提供一个 Accept 方法,接收访问者作为参数:
type Element interface {
Accept(visitor Visitor)
}
type TextElement struct {
Content string
}
func (t *TextElement) Accept(visitor Visitor) {
visitor.VisitText(t)
}
type ImageElement struct {
URL string
}
func (i *ImageElement) Accept(visitor Visitor) {
visitor.VisitImage(i)
}
定义访问者接口和具体实现
每个操作逻辑封装在一个访问者实现中:
type Visitor interface {
VisitText(*TextElement)
VisitImage(*ImageElement)
}
// 导出为HTML的操作
type HTMLExportVisitor struct{}
func (h *HTMLExportVisitor) VisitText(t *TextElement) {
print("" + t.Content + "
立即学习“go语言免费学习笔记(深入)”;
魔法映像企业网站管理系统
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
下载
")
}
func (h *HTMLExportVisitor) VisitImage(i *ImageElement) {
print("@@##@@")
}
// 统计字数的操作
type WordCountVisitor struct {
Count int
}
func (w *WordCountVisitor) VisitText(t *TextElement) {
words := strings.Split(strings.TrimSpace(t.Content), " ")
if len(words) > 0 && words[0] != "" {
w.Count += len(words)
}
}
func (w *WordCountVisitor) VisitImage(i *ImageElement) {
w.Count += 5 // 假设每张图相当于5个词
}
使用方式:灵活添加新操作
当需要新增功能时,比如添加“敏感词检查”,只需定义新的访问者,无需改动 TextElement 或 ImageElement:
type SensitiveCheckVisitor struct {
Found bool
}
func (s *SensitiveCheckVisitor) VisitText(t *TextElement) {
if strings.Contains(t.Content, "badword") {
s.Found = true
}
}
func (s *SensitiveCheckVisitor) VisitImage(i *ImageElement) {
if strings.Contains(i.URL, "unsafe") {
s.Found = true
}
}
调用示例:
elements := []Element{
&TextElement{Content: "Hello world"},
&ImageElement{URL: "logo.png"},
}
htmlVisitor := &HTMLExportVisitor{}
for _, e := range elements {
e.Accept(htmlVisitor)
}
wordCounter := &WordCountVisitor{}
for _, e := range elements {
e.Accept(wordCounter)
}
fmt.Println("Word count:", wordCounter.Count)
基本上就这些。通过访问者模式,Go语言也能很好地支持“在不修改结构的情况下动态添加操作”的需求。关键是让每个元素主动接受访问者,并调用对应的方法,从而把执行逻辑交给外部。这种方式在处理AST、文档模型、UI组件树等场景中非常实用。不复杂但容易忽略的是:必须为每种元素类型在每个访问者中实现对应方法,类型多了会有些样板代码。









