访问者模式通过分离操作与数据结构,使Go中复杂对象的多操作处理更灵活。定义Element接口的Accept方法和Visitor接口的Visit方法,让具体元素如Circle、Square在被访问时回调对应Visit实现,从而执行如计算面积、导出JSON等操作。示例中AreaCalculator累加面积,JSONExporter构建JSON字符串,均通过Accept-Visit机制遍历元素完成,新增操作无需修改元素类,符合开闭原则,提升代码可扩展性。

当我们在Go语言中处理那些由不同类型对象组成的复杂数据结构时,比如一个由各种图形(圆形、矩形)构成的文档,并且我们想对这些图形执行多种操作(计算面积、导出JSON、渲染),访问者模式提供了一个优雅的解决方案。它允许我们将操作逻辑与对象结构本身解耦,让我们的代码更具扩展性和维护性,避免了在每个对象里堆砌各种操作,或者写出冗长的类型判断链。简单来说,它让数据结构保持稳定,而操作可以灵活地增删。
要实践Golang的访问者模式,我们核心需要定义两套接口:
Element(元素)和
Visitor(访问者)。
Element接口会有一个
Accept方法,它接收一个
Visitor作为参数。具体的元素类型,比如
Circle或
Square,会实现这个
Accept方法,并在其中调用传入
Visitor对象的对应
Visit方法。而
Visitor接口则会为每一种具体的
Element类型定义一个
Visit方法。这样,当一个访问者“访问”一个元素时,元素的
Accept方法会回调访问者上针对该元素类型的方法,从而执行特定的操作。
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
package main
import (
"fmt"
"math"
"strings" // 用于处理JSON输出的逗号
)
// Element 接口:定义了一个接受访问者的方法
type Element interface {
Accept(v Visitor)
}
// Visitor 接口:为每种具体元素类型定义一个访问方法
// 如果有更多元素类型,这里需要扩展
type Visitor interface {
VisitCircle(c *Circle)
VisitSquare(s *Square)
}
// Concrete Elements
type Circle struct {
Radius float64
}
func (c *Circle) Accept(v Visitor) {
v.VisitCircle(c)
}
type Square struct {
Side float64
}
func (s *Square) Accept(v Visitor) {
v.VisitSquare(s)
}
// Concrete Visitors
type AreaCalculator struct {
TotalArea float64
}
func (ac *AreaCalculator) VisitCircle(c *Circle) {
ac.TotalArea += math.Pi * c.Radius * c.Radius
}
func (ac *AreaCalculator) VisitSquare(s *Square) {
ac.TotalArea += s.Side * s.Side
}
type JSONExporter struct {
Output strings.Builder // 使用strings.Builder更高效
}
func (je *JSONExporter) VisitCircle(c *Circle) {
je.Output.WriteString(fmt.Sprintf(`{"type": "circle", "radius": %.2f},`, c.Radius))
}
func (je *JSONExporter) VisitSquare(s *Square) {
je.Output.WriteString(fmt.Sprintf(`{"type": "square", "side": %.2f},`, s.Side))
}
func main() {
elements := []Element{
&Circle{Radius: 5},
&Square{Side: 10},
&Circle{Radius: 3},
}
fmt.Println("--- 计算总面积 ---")
areaCalc := &AreaCalculator{}
for _, elem := range elements {
elem.Accept(areaCalc)
}
fmt.Printf("总面积: %.2f\n", areaCalc.TotalArea)
fmt.Println("\n--- 导出为 JSON ---")
jsonExp := &JSONExporter{}
jsonExp.Output.WriteString("[") // 开始JSON数组
for _, elem := range elements {
elem.Accept(jsonExp)
}
// 移除末尾逗号(如果有),然后关闭数组
finalOutput := jsonExp.Output.String()
if len(finalOutput) > 1 && finalOutput[len(finalOutput









