訪問者模式屬於行為型模式,其核心思想是將數據結構與數據操作分離:在不修改數據結構的前提下,為其動態添加新的操作邏輯。本文通過「花朵與訪問者」經典案例,深度解析模式結構、角色職責、代碼實現及適用場景。

| 角色 | 職責 | 對應代碼 |
|---|---|---|
| Visitor (抽象訪問者) | 聲明訪問各元素的操作介面 | interface Visitor { visit(Gladiolus); visit(Chrysanthemum); } |
| ConcreteVisitor (具體訪問者) | 實現具體訪問邏輯 | StringVisitor (獲取名稱), BeeVisitor (模擬蜜蜂授粉) |
| Visitable/Element (抽象元素) | 聲明 accept(Visitor) 方法 | interface Flower { accept(Visitor); } |
| ConcreteElement (具體元素) | 實現 accept,調用 visitor.visit(this) | Gladiolus (劍蘭), Chrysanthemum (菊花) |
| ObjectStructure (對象結構) | 管理元素集合,提供遍歷入口 | List<Flower> + 迭代器遍歷 |
// 抽象訪問者
interface Visitor {
void visit(Gladiolus g);
void visit(Chrysanthemum c);
}
// 具體訪問者 1:獲取花名字符串
class StringVisitor implements Visitor {
private String result;
public String toString() { return result; }
public void visit(Gladiolus g) { result = "Gladiolus"; }
public void visit(Chrysanthemum c) { result = "Chrysanthemum"; }
}
// 具體訪問者 2:模擬蜜蜂訪問行為
class BeeVisitor implements Visitor {
public void visit(Gladiolus g) { System.out.println("蜜蜂來訪問劍蘭"); }
public void visit(Chrysanthemum c) { System.out.println("蜜蜂來訪問菊花"); }
}
// 抽象元素
interface Flower {
void accept(Visitor v);
}
// 具體元素:菊花
class Chrysanthemum implements Flower {
public void accept(Visitor v) { v.visit(this); } // 關鍵:雙分派入口
}
// 具體元素:劍蘭
class Gladiolus implements Flower {
public void accept(Visitor v) { v.visit(this); }
}
// 花朵生成器 (模擬對象結構)
class FlowerGenerator {
private static Random rand = new Random();
public static Flower newFlower() {
return rand.nextInt(2) == 0 ? new Gladiolus() : new Chrysanthemum();
}
}
public class VisitorPatternDemo {
public static void main(String[] args) {
// 1. 構建對象結構
List<Flower> flowers = new ArrayList<>();
for (int i = 0; i < 10; i++) flowers.add(FlowerGenerator.newFlower());
// 2. 訪問者 1:統計花名
Visitor nameVisitor = new StringVisitor();
for (Flower f : flowers) {
f.accept(nameVisitor);
System.out.println(nameVisitor);
}
System.out.println("---------------");
// 3. 訪問者 2:模擬蜜蜂行為 (新增操作,無需修改 Flower 類)
Visitor beeVisitor = new BeeVisitor();
for (Flower f : flowers) {
f.accept(beeVisitor);
}
}
}
Gladiolus
Chrysanthemum
Chrysanthemum
Gladiolus
...
---------------
蜜蜂來訪問劍蘭
蜜蜂來訪問菊花
蜜蜂來訪問菊花
蜜蜂來訪問劍蘭
...
訪問者模式本質上實現了雙分派:
element.accept(visitor),根據元素類型 (Gladiolus/Chrysanthemum) 分派到具體 accept 方法。accept 內部調用 visitor.visit(this),根據訪問者類型 (StringVisitor/BeeVisitor) 分派到具體 visit 重載方法。Java 單分派特性下,通過「元素主動調用訪問者方法並傳遞 this」曲線實現雙分派。
| 優點 | 缺點 |
|---|---|
| 符合單一職責:操作邏輯集中在訪問者 | 增加新元素類困難:需修改 Visitor 介面及所有實現 |
| 開閉原則:新增操作無需修改元素類 | 破壞封裝:訪問者需訪問元素內部狀態 (常需暴露 getter) |
| 複用性:同一對象結構可復用多訪問者 | 適用性受限:僅適合穩定結構、變化操作場景 |