意圖
表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作,它使你可以在不改變各元素的類的前提下,定義作用于這些元素的新操作。
結(jié)構(gòu)

訪問者結(jié)構(gòu)圖
適用性
- 一個(gè)對(duì)象結(jié)構(gòu)包含很多類對(duì)象,它們有不同的接口,而你想對(duì)這些對(duì)象實(shí)施一些依賴于其具體類的操作;
- 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作“污染”這些對(duì)象的類;
- 定義對(duì)象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。
優(yōu)缺點(diǎn)
- 訪問者模式使得易于增加新的操作,僅需增加一個(gè)新的訪問者即可在一個(gè)對(duì)象結(jié)構(gòu)上定義一個(gè)新的操作;
- 相關(guān)的行為被集中在訪問者中,無關(guān)的行為被分散到各自的訪問者子類中;
- 每增加一個(gè)新的Element類,就需要在Visitor及其子類中添加新的操作;
- 通過類層次進(jìn)行訪問,可以訪問具有不同父類的對(duì)象,可以對(duì)Visitor增加任意類型的對(duì)象;
- 當(dāng)訪問者訪問對(duì)象結(jié)構(gòu)中每一個(gè)元素時(shí),它能夠積累狀態(tài);
- 訪問者通常需要訪問每一個(gè)元素的內(nèi)部狀態(tài),因此很可能會(huì)破壞元素的封裝性。
示例
一個(gè)編譯器將源代碼轉(zhuǎn)換為抽象語法樹之后,需要對(duì)該抽象語法樹中的各個(gè)節(jié)點(diǎn)(變量節(jié)點(diǎn),賦值節(jié)點(diǎn)等)進(jìn)行類型檢查(TypeCheck)、格式打?。≒rettyPrint)及擴(kuò)展的各種操作。
實(shí)現(xiàn)(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("類型檢查「變量表達(dá)式」..");
}
public override void VisitAssignmentNode(AssignmentNode node)
{
Console.WriteLine("類型檢查「賦值表達(dá)式」..");
}
}
// 2. 格式打印
public class PrettyPrintingVisitor : NodeVisitor
{
public override void VisitVariableRefNode(VariableRefNode node)
{
Console.WriteLine("格式打印「變量表達(dá)式」..");
}
public override void VisitAssignmentNode(AssignmentNode node)
{
Console.WriteLine("格式打印「賦值表達(dá)式」..");
}
}
public class App
{
public static void Main(string[] args)
{
// 模擬一個(gè)對(duì)象結(jié)構(gòu)
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);
}
}
}
// 控制臺(tái)輸出:
// 類型檢查「變量表達(dá)式」..
// 類型檢查「賦值表達(dá)式」..
// 格式打印「變量表達(dá)式」..
// 格式打印「賦值表達(dá)式」..