訪問者模式將作用於數據結構的操作封裝為獨立的 Visitor 類,使新增操作無需修改數據結構類,符合開閉原則。核心角色:
visit(ConcreteElement)accept(Visitor) 授權入口accept(v) { v.visit(this); }(雙分派關鍵)List),提供遍歷入口
// 元素體系
abstract class Student {
private String name, university, rating;
// getter/setter 省略
public abstract void accept(Visitor visitor);
}
class Bachelor extends Student {
public void accept(Visitor v) { v.visit(this); }
}
class College extends Student {
public void accept(Visitor v) { v.visit(this); }
}
// 訪問者接口
interface Visitor {
void visit(Bachelor b);
void visit(College c);
}
// 具體訪問者:打印簡歷
class ShowVisitor implements Visitor {
public void visit(Bachelor b) {
System.out.println("本科生: " + b.getName());
printDetail(b);
}
public void visit(College c) {
System.out.println("專科生: " + c.getName());
printDetail(c);
}
private void printDetail(Student s) { ... }
}
// 具體訪問者:統計本科生數量
class SumVisitor implements Visitor {
private int bachelorCount = 0;
public void visit(Bachelor b) { bachelorCount++; }
public void visit(College c) { /* 忽略 */ }
public int getCount() { return bachelorCount; }
}
// 客戶端
List<Student> list = Arrays.asList(new Bachelor(), new College());
Visitor show = new ShowVisitor(), sum = new SumVisitor();
for (Student s : list) {
s.accept(show);
s.accept(sum);
}
System.out.println("本科生總數: " + sum.getCount());
| 維度 | Visitor | Iterator |
|---|---|---|
| 訪問對象類型 | 異構(不同 ConcreteElement) | 同構(同一接口/基類) |
| 依賴關係 | 依賴具體實現(違反 DIP) | 依賴抽象接口 |
| 擴展性 | 新增操作易(新 Visitor),新增元素難(改接口) | 新增元素易,新增操作難(違反 OCP) |
| 適用場景 | 數據結構穩定、操作頻繁變化 | 數據結構變化、操作固定 |
Java 單分派 = 靜態重載(編譯期根據參數靜態類型綁定)+ 動態分派(運行期根據接收者實際類型綁定)。普通方法調用 obj.method(arg) 僅根據 obj 動態分派。
訪問者模式通過兩次動態分派實現「操作隨訪問者類型與元素類型雙重變化」:
element.accept(visitor) → 根據 element 實際類型調用 Bachelor.accept() 或 College.accept()visitor.visit(this) → 根據 visitor 實際類型調用 ShowVisitor.visit(Bachelor) 或 SumVisitor.visit(Bachelor)編譯器靜態綁定保證 visit(this) 參數類型為具體元素類型,從而選中正確重載版本。
Javac 詞法/語法分析生成 AST(抽象語法樹),後續語義分析、數據流分析、插糖、字節碼生成等階段均採用 Visitor 模式遍歷 AST:
TreeScanner:基礎遍歷器,空操作TreeVisitor:語義分析(類型檢查、符號表填充)Flow:數據流分析(確定賦值、到達性)Lower:插糖(Lambda、switch 表達式、記錄類降級)Gen:字節碼生成AST 節點(JCTree 子類)結構穩定,分析任務隨 JDK 版本頻繁擴展,完美契合「數據結構固定、操作易變」特征。
優點:單一職責、開閉原則(新增操作)、集中相關行為、可跨層級訪問(如遍歷組合模式樹)。
缺點:違反依賴倒置(依賴具體元素)、新增元素類需修改 Visitor 接口及所有實現、破壞封裝(需暴露內部狀態給 Visitor)。
適用判斷:若對象結構很少變化,但經常需要在其上定義新操作(報導、導出、分析、優化),優先考慮 Visitor;若結構頻繁增減,改用解释器模式或直接在元素類中添加方法。