意图
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下,定义作用于这些元素的新操作。
结构
适用性
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作;
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类;
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
优缺点
- 访问者模式使得易于增加新的操作,仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作;
- 相关的行为被集中在访问者中,无关的行为被分散到各自的访问者子类中;
- 每增加一个新的Element类,就需要在Visitor及其子类中添加新的操作;
- 通过类层次进行访问,可以访问具有不同父类的对象,可以对Visitor增加任意类型的对象;
- 当访问者访问对象结构中每一个元素时,它能够积累状态;
- 访问者通常需要访问每一个元素的内部状态,因此很可能会破坏元素的封装性。
示例
一个编译器将源代码转换为抽象语法树之后,需要对该抽象语法树中的各个节点(变量节点,赋值节点等)进行类型检查(TypeCheck)、格式打印(PrettyPrint)及扩展的各种操作。
实现(C#)
using System;
public abstract class Node
{
public abstract void Accept(NodeVisitor visitor);
}
public class VariableRefNode : Node
{
public override void Accept(NodeVisitor visitor)
{
visitor.VisitVariableRefNode(this);
}
}
public class AssignmentNode : Node
{
public override void Accept(NodeVisitor visitor)
{
visitor.VisitAssignmentNode(this);
}
}
public abstract class NodeVisitor
{
public abstract void VisitVariableRefNode(VariableRefNode node);
public abstract void VisitAssignmentNode(AssignmentNode node);
}
// 1. 类型检查
public class TypeCheckingVisitor : NodeVisitor
{
public override void VisitVariableRefNode(VariableRefNode node)
{
Console.WriteLine("类型检查「变量表达式」..");
}
public override void VisitAssignmentNode(AssignmentNode node)
{
Console.WriteLine("类型检查「赋值表达式」..");
}
}
// 2. 格式打印
public class PrettyPrintingVisitor : NodeVisitor
{
public override void VisitVariableRefNode(VariableRefNode node)
{
Console.WriteLine("格式打印「变量表达式」..");
}
public override void VisitAssignmentNode(AssignmentNode node)
{
Console.WriteLine("格式打印「赋值表达式」..");
}
}
public class App
{
public static void Main(string[] args)
{
// 模拟一个对象结构
Node[] objectStructure = { new VariableRefNode(), new AssignmentNode() };
// 1. 类型检查
objectStructure.Accept(new TypeCheckingVisitor());
// 2. 格式打印
objectStructure.Accept(new PrettyPrintingVisitor());
}
}
public static class NodeExtensions
{
public static void Accept(this Node[] nodes, NodeVisitor visitor)
{
foreach(Node node in nodes)
{
node.Accept(visitor);
}
}
}
// 控制台输出:
// 类型检查「变量表达式」..
// 类型检查「赋值表达式」..
// 格式打印「变量表达式」..
// 格式打印「赋值表达式」..