當需要對一個對象結構(如抽象語法樹、文檔對象模型)中的元素執行多種不相關操作(編譯、優化、打印、序列化)時,若直接在元素類中添加方法,會導致類膨脹且違反單一職責。訪問者模式將操作從元素類中分離,封裝到獨立的訪問者類中,新增操作無需修改元素類(開閉原則)。
Java 只有單分派(動態綁定僅基於接收者類型)。訪問者模式通過兩次單分派模擬雙重分派:
element.accept(visitor) —— 第一次分派,基於 element 實際類型調用具體 accept。accept 內部調用 visitor.visit(this) —— 第二次分派,基於 visitor 實際類型調用具體 visit(ConcreteElement)。最終執行的代碼同時取決於 Element 和 Visitor 的具體類型。
| 角色 | 接口/類 | 關鍵方法 |
|---|---|---|
| Visitor (抽象訪問者) | interface | visit(ConcreteElementA), visit(ConcreteElementB) |
| ConcreteVisitor (具體訪問者) | class | 實現各 visit,封裝一類操作邏輯 |
| Element (抽象元素) | abstract class | accept(Visitor v) |
| ConcreteElement (具體元素) | class | 實現 accept → v.visit(this),暴露自身特有方法 |
| ObjectStructure (對象結構) | class | 管理元素集合,提供 action(Visitor) 遍歷調用 accept |
public interface Visitor {
void visit(NodeA node);
void visit(NodeB node);
}
public class VisitorA implements Visitor {
@Override public void visit(NodeA node) { System.out.println("VisitorA 處理 " + node.operationA()); }
@Override public void visit(NodeB node) { System.out.println("VisitorA 處理 " + node.operationB()); }
}
public class VisitorB implements Visitor {
@Override public void visit(NodeA node) { System.out.println("VisitorB 處理 " + node.operationA()); }
@Override public void visit(NodeB node) { System.out.println("VisitorB 處理 " + node.operationB()); }
}
public abstract class Node {
public abstract void accept(Visitor visitor);
}
public class NodeA extends Node {
@Override public void accept(Visitor v) { v.visit(this); }
public String operationA() { return "NodeA 業務邏輯"; }
}
public class NodeB extends Node {
@Override public void accept(Visitor v) { v.visit(this); }
public String operationB() { return "NodeB 業務邏輯"; }
}
public class ObjectStructure {
private List<Node> nodes = new ArrayList<>();
public void add(Node node) { nodes.add(node); }
public void action(Visitor visitor) {
for (Node node : nodes) node.accept(visitor);
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new NodeA());
os.add(new NodeB());
Visitor v = new VisitorA();
os.action(v); // 輸出 VisitorA 處理 NodeA/NodeB
}
}
Client → ObjectStructure.action(VisitorA)
→ NodeA.accept(VisitorA) // 第一次分派
→ VisitorA.visit(NodeA) // 第二次分派
→ NodeA.operationA()
→ NodeB.accept(VisitorA)
→ VisitorA.visit(NodeB)
→ NodeB.operationB()
| 優點 | 缺點 |
|---|---|
| 擴展性極強:新增操作只需新增 Visitor 子類,元素類零修改 | 元素類結構變動困難:新增/刪除 Element 需修改 Visitor 接口及所有實現類 |
| 復用性好:將相關行為集中在一個 Visitor 中 | 破壞封裝:Visitor 需訪問元素內部狀態,元素需暴露公共方法 |
| 分離關注點:編譯、打印、類型檢查等邏輯物理隔離 | 不適用於頻繁變動的元素結構 |
instanceof 判斷鏈。
public class TypeCheckVisitor implements Visitor {
private SymbolTable symTab = new SymbolTable();
private List<Error> errors = new ArrayList<>();
// visit 方法中可訪問/修改 symTab、errors
public List<Error> getErrors() { return errors; }
}
訪問者可攜帶上下文狀態(符號表、錯誤列表、生成代碼緩衝區),實現複雜的多遍處理。