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 ..> Visitornote right of ElementA::accept調用visitor.visitElementA(this)實現雙重分派機制
end note
@enduml
二、特點分析
優點
- 擴展性強:新增訪問者不影響現有系統
- 職責清晰:相關操作集中管理
- 復合操作:支持跨元素的復雜操作
缺點
- 破壞封裝:需暴露元素內部細節
- 維護困難:元素接口變更影響所有訪問者
- 適用局限:適合穩定元素結構的系統
三、適用場景
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_demoimport "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 errorVisitForAPI(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 元素遍歷 | 文件系統管理 |
七、實現建議
- 訪問者接口設計
type Visitor interface {VisitTypeA(*TypeA)VisitTypeB(*TypeB)DefaultVisit(interface{})
}
- 元素繼承處理
type BaseElement struct{}func (b *BaseElement) Accept(v Visitor) {v.DefaultVisit(b)
}
- 循環引用處理
type Element struct {visitor Visitor `json:"-"` // 避免序列化循環
}
- 訪問者狀態管理
type StatefulVisitor struct {buffer strings.Builder
}
八、典型應用
- 編譯器構建:語法樹檢查/優化
- 數據分析:異構數據集合統計
- 游戲引擎:場景實體遍歷更新
- 文檔處理:多格式導出系統
在Go語言中實現建議:
- 使用接口組合代替繼承
- 通過類型斷言實現泛型訪問者
- 結合通道實現并發訪問
- 使用sync.Pool優化高頻訪問對象