广告

Golang 访问者模式实战:实现数据与操作分离的完整方案

1. 背景与设计目标

在 Golang 语境下,Golang 访问者模式实战的目标是通过将数据结构与对数据的操作分离来实现高内聚、低耦合的设计。访问者模式允许在不修改数据结构的前提下,通过新增的 访问者 来扩展行为,因此可以更容易地实现新功能。本文以实现数据与操作分离的完整方案为目标,展示如何在 Go 语言 中落地这一设计。

核心设计目标包括:解耦数据结构与行为可扩展性提升、以及对变化的更小影响面。通过把操作放在 Visitor 实现中,数据结构保持纯粹,便于测试与复用。

2. 访问者模式核心概念

2.1 访问者角色与职责

在访问者模式中,Element(元素)只负责数据承载与对访问者的对接,不包含具体的行为逻辑;Visitor(访问者)负责实现对不同数据元素的具体操作。此分工实现了对行为的横向扩展。

通过 Accept 方法,数据元素把自己暴露给访问者,从而把数据与操作解耦,避免在数据模型中嵌入操作的实现

2.2 数据结构与操作分离的原理

核心原理是把“要对数据执行的行为”从数据结构中抽离出来,放到独立的 Visitor 实现中。这样,当需要增加新操作时,无需修改现有 数据结构,只需新增一个实现了 Visitor 接口 的类即可。

在实践中,遍历对象结构 的职责也可以由一个容器对象(如 DataCollection)来承担,形成一个完整的遍历流程,确保数据与操作的单一职责

3. Golang 实现要点

3.1 接口设计与角色划分

用 Go 实现时,Interface(接口)是核心,Element 与 Visitor 形成明确的交互边界。数据元素实现 Accept(v Visitor),把控制权交给访问者,避免在元素内部嵌入具体操作。

设计要点包括:简单的 Accept 调用、对多种数据元素的扩展友好、以及避免在同一类型中混杂多种行为。

3.2 数据集合与遍历

当存在多个元素时,数据集合对象(如 DataCollection)负责统一遍历,将每个元素暴露给访问者。这种结构使得新增元素不会破坏现有遍历逻辑。

此外,可测试性可重用性在此设计中显著提升,因为操作实现独立于数据模型。

4. 数据与操作分离的完整实现示例

下面给出一个可直接编译运行的 Go 语言实现,展示如何在 Golang 访问者模式实战中实现数据与操作的分离,以及如何在一个简单场景中完成可扩展的操作。你可以直接复制代码,结合实际数据结构扩展更多元素和操作。

package main

import "fmt"

// Element 是访问者要“访问”的对象,但不包含具体访问逻辑
type Element interface {
    Accept(v Visitor)
}

// DataElement 是具体数据结构,负责承载数据,只暴露数据,不实现访问逻辑
type DataElement struct {
    ID    string
    Name  string
    Value int
}

func (d *DataElement) Accept(v Visitor) { v.VisitDataElement(d) }

// DataCollection 作为数据集合,游走时会把每个元素交给访问者处理
type DataCollection struct {
    Elements []Element
}

func (c *DataCollection) Accept(v Visitor) {
    for _, e := range c.Elements {
        e.Accept(v)
    }
}

// Visitor 定义了对不同数据元素的操作入口
type Visitor interface {
    VisitDataElement(*DataElement)
}

// Concrete Visitor:实现具体操作(如格式化输出、聚合统计等)
type PrinterVisitor struct {
    Output []string
}

func (p *PrinterVisitor) VisitDataElement(d *DataElement) {
    p.Output = append(p.Output, fmt.Sprintf("DataElement{ID=%s, Name=%s, Value=%d}", d.ID, d.Name, d.Value))
}

// Demonstration usage
func main() {
    items := []Element{
        &DataElement{ID: "A1", Name: "temperature", Value: 23},
        &DataElement{ID: "A2", Name: "pressure", Value: 1013},
        &DataElement{ID: "A3", Name: "humidity", Value: 56},
    }

    coll := &DataCollection{Elements: items}
    v := &PrinterVisitor{}

    coll.Accept(v)

    for _, line := range v.Output {
        fmt.Println(line)
    }
}

通过上面的实现可以看到,数据结构保持干净,操作逻辑集中在访问者中,扩展新操作变得简单且风险低。

广告

后端开发标签