當需對包含不同類型元素的聚集執行操作,且操作細節隨元素類型異變時,往往需大量類型判斷。訪問者模式將操作封裝於訪問者對象,數據結構保持穩定,實現「數據結構」與「操作邏輯」解耦。
核心過程:節點調用訪問者的 Visit 方法並傳入自身 → 訪問者反向調用節點的具體操作。此「雙重分派」使操作同時基於訪問者類型與節點類型決定。

Accept(Visitor) 方法。visitor.Visit(this)。
// Visitor pattern -- Structural example
using System; using System.Collections;
abstract class Visitor {
abstract public void VisitConcreteElementA(ConcreteElementA e);
abstract public void VisitConcreteElementB(ConcreteElementB e);
}
class ConcreteVisitor1 : Visitor {
override public void VisitConcreteElementA(ConcreteElementA e) { Console.WriteLine("{0} visited by {1}", e, this); }
override public void VisitConcreteElementB(ConcreteElementB e) { Console.WriteLine("{0} visited by {1}", e, this); }
}
class ConcreteVisitor2 : Visitor {
override public void VisitConcreteElementA(ConcreteElementA e) { Console.WriteLine("{0} visited by {1}", e, this); }
override public void VisitConcreteElementB(ConcreteElementB e) { Console.WriteLine("{0} visited by {1}", e, this); }
}
abstract class Element { abstract public void Accept(Visitor v); }
class ConcreteElementA : Element {
public override void Accept(Visitor v) { v.VisitConcreteElementA(this); }
public void OperationA() {}
}
class ConcreteElementB : Element {
public override void Accept(Visitor v) { v.VisitConcreteElementB(this); }
public void OperationB() {}
}
class ObjectStructure {
private ArrayList elements = new ArrayList();
public void Attach(Element e) { elements.Add(e); }
public void Detach(Element e) { elements.Remove(e); }
public void Accept(Visitor v) { foreach (Element e in elements) e.Accept(v); }
}
public class Client {
public static void Main() {
ObjectStructure o = new ObjectStructure();
o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB());
Visitor v1 = new ConcreteVisitor1(), v2 = new ConcreteVisitor2();
o.Accept(v1); o.Accept(v2);
}
}
// Visitor pattern -- Real World example
using System; using System.Collections;
abstract class Visitor { abstract public void Visit(Element e); }
class IncomeVisitor : Visitor {
public override void Visit(Element e) {
Employee emp = (Employee)e;
emp.Income *= 1.10; // 10% 加薪
Console.WriteLine("{0}'s new income: {1:C}", emp.Name, emp.Income);
}
}
class VacationVisitor : Visitor {
public override void Visit(Element e) {
Employee emp = (Employee)e;
emp.VacationDays += 3; // 增加 3 天假期
Console.WriteLine("{0}'s new vacation days: {1}", emp.Name, emp.VacationDays);
}
}
abstract class Element { abstract public void Accept(Visitor v); }
class Employee : Element {
string name; double income; int vacationDays;
public Employee(string name, double income, int vacationDays) {
this.name = name; this.income = income; this.vacationDays = vacationDays;
}
public string Name { get { return name; } set { name = value; } }
public double Income { get { return income; } set { income = value; } }
public int VacationDays { get { return vacationDays; } set { vacationDays = value; } }
public override void Accept(Visitor v) { v.Visit(this); }
}
class Employees {
private ArrayList employees = new ArrayList();
public void Attach(Employee e) { employees.Add(e); }
public void Detach(Employee e) { employees.Remove(e); }
public void Accept(Visitor v) { foreach (Employee e in employees) e.Accept(v); }
}
public class VisitorApp {
public static void Main() {
Employees e = new Employees();
e.Attach(new Employee("Hank", 25000.0, 14));
e.Attach(new Employee("Elly", 35000.0, 16));
e.Attach(new Employee("Dick", 45000.0, 21));
Visitor v1 = new IncomeVisitor(), v2 = new VacationVisitor();
e.Accept(v1); e.Accept(v2);
}
}
適用場景:
核心優勢:
主要缺點:
決策原則: 僅在「數據結構穩定、操作頻繁變化」時使用。若節點類頻繁新增,請避免訪問者模式。