初探访问者模式

代码链接

koonchen/design-patterns/visitor

要点

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

例如,一个不使用访问者的编译器可能会通过在它的抽象语法树上调用 TypeCheck 操作对一个过程进行类型检查。每一个结点将对调用它的成员的 TypeCheck 以实现自身的 TypeCheck

每一个结点在实现 Accept 时将会回调访问者:一个赋值结点调用访问者的 VisitAssignment 操作,而一个变量引用将调用 VisitVariableReference ,以前类 AssignmentNodeTypeCheck 操作现在成为 TypeCheckingVisitorVisitAssignment 操作。

为使访问者不仅仅只做类型检查,我们需要所有抽象语法树的访问者有一个抽象的父类 NodeVisitorVisitor 模式将每一个编译步骤的操作封装在一个与该步骤相关的 Visitor 中。

使用 Visitor 模式,必须定义两个类层次:一个对应于接受操作的元素,另一个对应于定义对元素的操作的访问者。

cover

访问者模式的适用性:

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作污染这些对象的类。
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。

访问者模式的参与者:

  • Visitor — 为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。
  • ConcreteVistor — 实现每一个由 Visitor 声明的操作。
  • Element — 定义一个 Accept 操作,它以一个访问者为参数。
  • ConcreteElement — 实现 Accept 操作。
  • ObjectStructure — 能枚举它的元素。

访问者模式的结构:

cover

访问者模式的效果:

  • 访问者模式使得易于增加新的操作。
  • 访问者集中相关的操作而分离无关的操作。
  • 增加新的 ConcreteElement 类很困难。
  • 通过类层次进行访问。
  • 累计状态 — 当访问者访问对象结构中的每一个元素时,它可能会累计状态。
  • 破坏封装 — 访问者方法假定 ConcreteElement 接口的功能足够强,足以让访问者进行它的工作。

实现

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface Visitor {
void visitConcreteElement(Element element);
}

class ConcreteVisitor implements Visitor {

@Override
public void visitConcreteElement(Element element) {
element.doSth();
}
}

interface Element {
void accept(Visitor visitor);
void doSth();
}

class ConcreteElement implements Element {

@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElement(this);
}

@Override
public void doSth() {
System.out.println("the method has been used");
}
}

public class VisitorClient {
public static void main(String[] args) {
// 省略存储全部的 element
Element element = new ConcreteElement();
Visitor visitor = new ConcreteVisitor();
element.accept(visitor);
}
}

相关模式:

Composite : 访问者可以用于对一个由 Composite 模式定义的对象结构进行操作。

Interpreter : 范文者可以用于解释。