设计模式-21-访问者模式

访问者模式是一种行为设计模式,它允许在不改变数据结构的情况下,定义新的操作(访问者)并应用于数据结构中的元素。在这种模式下,我们将数据结构和操作之间的耦合分离,使得新增操作时不需要修改元素的类

访问者模式的核心思想是将操作封装在访问者对象中,而不是分散在被访问的对象中。被访问的对象通过接受访问者的访问,将自身作为参数传递给访问者,从而实现对自身操作的解耦和扩展

它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很 少被用到,在没有特别必要的情况下,建议不要使用访问者模式。

逻辑结构

Visitor

代码实现

代码路径:https://github.com/XBoom/DesignPatterns/tree/main/20_Visitor

  1. 首先,定义一个访问者接口 Visitor,表示访问不同的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Visitor 访问者接口
    type Visitor interface {
    VisitConcreteElementA(element *ConcreteElementA)
    VisitConcreteElementB(element *ConcreteElementB)
    }

    // ConcreteVisitor 具体访问者
    type ConcreteVisitor struct{}

    func (v *ConcreteVisitor) VisitConcreteElementA(element *ConcreteElementA) {
    fmt.Println("访问者正在访问具体元素 A")
    }

    func (v *ConcreteVisitor) VisitConcreteElementB(element *ConcreteElementB) {
    fmt.Println("访问者正在访问具体元素 B")
    }
  2. 然后,定义多个具体元素结构体,它们实现了接受访问者访问的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // Element 元素接口
    type Element interface {
    Accept(visitor Visitor)
    }

    // ConcreteElementA 具体元素 A
    type ConcreteElementA struct{}

    func (e *ConcreteElementA) Accept(visitor Visitor) {
    visitor.VisitConcreteElementA(e)
    }

    // ConcreteElementB 具体元素 B
    type ConcreteElementB struct{}

    func (e *ConcreteElementB) Accept(visitor Visitor) {
    visitor.VisitConcreteElementB(e)
    }
  3. 接着,定义一个具体的访问者结构体,它实现了访问者接口中定义的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // ObjectStructure 对象结构
    type ObjectStructure struct {
    elements []Element
    }

    func (o *ObjectStructure) Attach(element Element) {
    o.elements = append(o.elements, element)
    }

    func (o *ObjectStructure) Accept(visitor Visitor) {
    for _, element := range o.elements {
    element.Accept(visitor)
    }
    }

运行

1
2
3
4
5
6
objectStructure := &ObjectStructure{}
objectStructure.Attach(&ConcreteElementA{})
objectStructure.Attach(&ConcreteElementB{})

visitor := &ConcreteVisitor{}
objectStructure.Accept(visitor)

结果

1
2
访问者正在访问具体元素 A
访问者正在访问具体元素 B

适用场景

  1. 当一个对象的结构较为稳定,但对该对象的操作却经常变化时,使用访问者模式可以将操作的变化封装在访问者对象中,而不影响元素对象的稳定性。
  2. 当需要对一个对象结构中的多个元素进行操作,并且这些操作具有一定的关联性时,可以使用访问者模式将这些操作封装在一个访问者中,提高代码的复用性和可维护性。
  3. 当对象结构中的元素类数量较少且固定时,使用访问者模式可以简化代码结构,将元素的操作集中在访问者中,减少代码的分散性。

总结

  • 开闭原则。 可以引入在不同类对象上执行的新行为, 且无需对这些类做出修改。
  • 单一职责原则。 可将同一行为的不同版本移到同一个类中。
  • 访问者对象可以在与各种对象交互时收集一些有用的信息。 当你想要遍历一些复杂的对象结构 (例如对象树), 并在结构中的每个对象上应用访问者时, 这些信息可能会有所帮助

参考链接

  1. https://refactoringguru.cn/design-patterns/visitor
  2. https://lailin.xyz/post/visitor.html