当前位置: 首页 > article >正文

Golang学习笔记_47——访问者模式

Golang学习笔记_44——命令模式
Golang学习笔记_45——备忘录模式
Golang学习笔记_46——状态模式


文章目录

    • 一、核心概念
      • 1. 定义
      • 2. 解决的问题
      • 3. 核心角色
      • 4. 类图
    • 二、特点分析
    • 三、适用场景
      • 1. 编译器实现
      • 2. 财务系统
      • 3. UI组件系统
    • 四、Go语言实现示例
      • 完整实现代码
      • 执行结果
    • 五、高级应用
      • 1. 异步访问者
      • 2. 动态派发优化
    • 六、与其他模式对比
    • 七、实现建议
    • 八、典型应用


一、核心概念

1. 定义

访问者模式是一种行为型设计模式,允许在不修改已有对象结构的前提下定义新的操作。其核心特点包括:
操作解耦:将数据操作与数据结构分离
双重分发:通过两次方法调用实现动态绑定
扩展开放:新增操作无需修改现有类

2. 解决的问题

操作污染:频繁添加新操作导致类不断膨胀
聚合困难:相关操作分散在不同类中
类型检查:避免大量instanceof类型判断

3. 核心角色

角色作用
Visitor声明访问操作的接口(visit方法)
ConcreteVisitor实现具体访问逻辑
Element定义接受访问者的接口(accept方法)
ConcreteElement实现accept方法的具体元素
ObjectStructure包含元素集合的容器

4. 类图

访问者模式类图

@startuml
interface Visitor {
    + visitElementA(e: ElementA)
    + visitElementB(e: ElementB)
}

class TaxVisitor {
    + visitElementA()
    + visitElementB()
}

interface Element {
    + accept(v: Visitor)
}

class ElementA {
    + accept(v: Visitor)
    + operationA()
}

class ElementB {
    + accept(v: Visitor)
    + operationB()
}

Visitor <|.. TaxVisitor
Element <|.. ElementA
Element <|.. ElementB
ElementA ..> Visitor
ElementB ..> Visitor

note right of ElementA::accept
    调用visitor.visitElementA(this)
    实现双重分派机制
end note
@enduml

二、特点分析

优点

  1. 扩展性强:新增访问者不影响现有系统
  2. 职责清晰:相关操作集中管理
  3. 复合操作:支持跨元素的复杂操作

缺点

  1. 破坏封装:需暴露元素内部细节
  2. 维护困难:元素接口变更影响所有访问者
  3. 适用局限:适合稳定元素结构的系统

三、适用场景

1. 编译器实现

type ASTVisitor interface {
    VisitVariableDecl(n *VariableDecl)
    VisitFunctionCall(n *FunctionCall)
}

type TypeChecker struct{}

func (t *TypeChecker) VisitVariableDecl(n *VariableDecl) {
    fmt.Printf("校验变量 %s 的类型\n", n.Name)
}

2. 财务系统

type FinancialVisitor interface {
    VisitInvoice(i *Invoice)
    VisitPayment(p *Payment)
}

type TaxCalculator struct{}

func (t *TaxCalculator) VisitInvoice(i *Invoice) {
    fmt.Printf("计算发票税款:%.2f\n", i.Amount*0.1)
}

3. UI组件系统

type UIVisitor interface {
    VisitButton(b *Button)
    VisitPanel(p *Panel)
}

class Renderer struct{}

func (r *Renderer) VisitButton(b *Button) {
    fmt.Printf("渲染按钮:%s\n", b.Label)
}

四、Go语言实现示例

示例代码类图

完整实现代码

package visitor_demo

import "fmt"

// Visitor 接口
type ComputerPartVisitor interface {
	VisitMouse(m *Mouse)
	VisitKeyboard(k *Keyboard)
}

// Concrete Visitor
type DisplayVisitor struct{}

func (d *DisplayVisitor) VisitMouse(m *Mouse) {
	fmt.Println("显示鼠标信息:", m.GetSpec())
}

func (d *DisplayVisitor) VisitKeyboard(k *Keyboard) {
	fmt.Println("显示键盘信息:", k.GetLayout())
}

// Element 接口
type ComputerPart interface {
	Accept(visitor ComputerPartVisitor)
}

// Concrete Elements
type Mouse struct {
	dpi int
}

func (m *Mouse) Accept(visitor ComputerPartVisitor) {
	visitor.VisitMouse(m)
}

func (m *Mouse) GetSpec() string {
	return fmt.Sprintf("%d DPI", m.dpi)
}

type Keyboard struct {
	layout string
}

func (k *Keyboard) Accept(visitor ComputerPartVisitor) {
	visitor.VisitKeyboard(k)
}

func (k *Keyboard) GetLayout() string {
	return k.layout
}

// Object Structure
type Computer struct {
	parts []ComputerPart
}

func (c *Computer) AddPart(p ComputerPart) {
	c.parts = append(c.parts, p)
}

func (c *Computer) Accept(visitor ComputerPartVisitor) {
	for _, p := range c.parts {
		p.Accept(visitor)
	}
}

// 客户端使用示例
func ExampleUsage() {
	computer := &Computer{
		parts: []ComputerPart{
			&Mouse{dpi: 1600},
			&Keyboard{layout: "QWERTY"},
		},
	}

	visitor := &DisplayVisitor{}
	computer.Accept(visitor)
}

执行结果

=== RUN   TestExampleUsage
显示鼠标信息: 1600 DPI
显示键盘信息: QWERTY
--- PASS: TestExampleUsage (0.00s)
PASS

五、高级应用

1. 异步访问者

type AsyncVisitor interface {
    VisitForDB(n Node) <-chan error
    VisitForAPI(n Node) <-chan error
}

func BatchVisit(nodes []Node, v AsyncVisitor) []error {
    // 实现并发访问处理
}

2. 动态派发优化

type DynamicVisitor struct {
    handlers map[reflect.Type]func(interface{})
}

func (dv *DynamicVisitor) Register(t reflect.Type, fn func(interface{})) {
    dv.handlers[t] = fn
}

六、与其他模式对比

模式核心区别典型应用场景
策略模式单算法选择 vs 多元素操作支付方式选择
装饰器模式增强功能 vs 添加操作IO流处理
组合模式树形结构 vs 元素遍历文件系统管理

七、实现建议

  1. 访问者接口设计
type Visitor interface {
    VisitTypeA(*TypeA)
    VisitTypeB(*TypeB)
    DefaultVisit(interface{})
}
  1. 元素继承处理
type BaseElement struct{}

func (b *BaseElement) Accept(v Visitor) {
    v.DefaultVisit(b)
}
  1. 循环引用处理
type Element struct {
    visitor Visitor `json:"-"` // 避免序列化循环
}
  1. 访问者状态管理
type StatefulVisitor struct {
    buffer strings.Builder
}

八、典型应用

  1. 编译器构建:语法树检查/优化
  2. 数据分析:异构数据集合统计
  3. 游戏引擎:场景实体遍历更新
  4. 文档处理:多格式导出系统

在Go语言中实现建议:

  • 使用接口组合代替继承
  • 通过类型断言实现泛型访问者
  • 结合通道实现并发访问
  • 使用sync.Pool优化高频访问对象

http://www.kler.cn/a/576721.html

相关文章:

  • 在K8S中,svc底层是如何实现的?
  • 【vitepress】如何搭建并部署自己的博客网站
  • 【树莓派Pico设备驱动】-BMP280环境传感器驱动(SPI方式)
  • 一种批量将本地word文档转成html的方法【基于node.js】
  • Java基础面试题全集
  • k8s概念及k8s集群部署(Centos7)
  • docker中kibana启动后,通过浏览器访问,出现server is not ready yet
  • SQL语法通用解析
  • 爬虫面试:关于爬虫破解验证码的13个经典面试题
  • 【网工面试】网络工程师面试问题与答案(一)
  • 电脑总显示串口正在被占用处理方法
  • C# | 委托 | 事件 | 异步
  • selenium 组成和原理
  • 【JavaEE】-- 多线程(初阶)4
  • Springboot集成dubbo完整过程(三)
  • nginx配置自动更新dns缓存
  • 【每日学点HarmonyOS Next知识】Web跨域资源、Web长按菜单、Web拦截请求、禁止录屏、Base64图片宽高
  • 蓝桥备赛(12)- 顺序表和 vector(下)
  • java中小型公司面试预习资料(二):Redis
  • 执行计划 统计信息相关 SQL_MONITOR display_cursor