首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 設計模式:訪問者模式深度解析與實戰

     編輯:Java 相關知識

模式動機:集合中的類型擦除痛點

Java 集合(Collection、List、Map)存儲的是 Object 參考。取出元素時,編譯期類型資訊丟失,若要針對不同類型執行不同邏輯,往往陷入 instanceof + 強制轉型的「判斷地獄」:


// 反模式:充滿 instanceof 的處理邏輯
Iterator it = collection.iterator();
while (it.hasNext()) {
    Object o = it.next();
    if (o instanceof Collection)      handleCollection((Collection)o);
    else if (o instanceof String)     handleString((String)o);
    else if (o instanceof Float)      handleFloat((Float)o);
    else                              handleDefault(o);
}

缺點:違反開閉原則——新增類型需修改遍歷邏輯;職責分散——同一類型的處理邏輯分散在各處。

訪問者模式核心思想

將資料結構(元素)與作用於其上的操作(訪問者)分離。在不修改元素類別的前提下,定義新的操作。

雙重分派機制

訪問者模式利用雙重分派實現「元素類型 + 訪問者類型」決定具體執行邏輯:

  1. 客戶端呼叫 element.accept(visitor) —— 第一次分派,綁定具體元素類型
  2. 元素實作呼叫 visitor.visitConcreteElement(this) —— 第二次分派,綁定具體訪問者類型

角色結構

角色介面/類別關鍵方法
Visitor介面visitConcreteElementA(A), visitConcreteElementB(B)...
ConcreteVisitor實作類實現各元素的具體操作邏輯
Visitable (Element)介面accept(Visitor)
ConcreteElement實作類實現 accept 呼叫對應的 visitXxx
ObjectStructure類別管理元素集合,提供遍歷入口

完整實戰程式碼

1. 訪問者介面


public interface Visitor {
    void visitCollection(Collection collection);
    void visitString(String string);
    void visitFloat(Float floatVal);
}

2. 可訪問元素介面


public interface Visitable {
    void accept(Visitor visitor);
}

3. 具體元素:包裝基本類型


public class ConcreteElement implements Visitable {
    private String value;
    
    public ConcreteElement(String value) { this.value = value; }
    public String getValue() { return value; }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visitString(this); // 關鍵:雙重分派第二步
    }
}

// 同理可定義 CollectionElement、FloatElement 等

4. 具體訪問者:集中處理邏輯


public class ConcreteVisitor implements Visitor {
    @Override
    public void visitCollection(Collection collection) {
        Iterator it = collection.iterator();
        while (it.hasNext()) {
            Object o = it.next();
            if (o instanceof Visitable) { // 僅需判斷是否可訪問
                ((Visitable) o).accept(this);
            }
        }
    }
    
    @Override
    public void visitString(String string) {
        System.out.println("'" + string + "'");
    }
    
    @Override
    public void visitFloat(Float floatVal) {
        System.out.println(floatVal + "f");
    }
}

5. 客戶端使用


List elements = Arrays.asList(
    new ConcreteElement("Hello"),
    new ConcreteElement("World"),
    new FloatElement(3.14f)
);

Visitor visitor = new ConcreteVisitor();
for (Visitable e : elements) {
    e.accept(visitor);
}
// 輸出:'Hello'  'World'  3.14f

適用條件與權衡

適用不適用
元素類型穩定(很少新增),但操作頻繁擴展元素類型頻繁新增(每新增一類型需修改 Visitor 介面及所有實作)
需對結構中不同類型物件執行不相關操作操作簡單、數量少,直接在元素類別加方法更清晰
物件結構複雜(樹狀、圖狀),需統一遍歷邏輯結構單純(單層 List),for-each 即可

打破限制:反射技術實現動態訪問者

當元素類型也頻繁變動時,可利用 Java 反射動態查找 visitXxx 方法,避免修改 Visitor 介面:


public class ReflectiveVisitor implements Visitor {
    @Override
    public void visit(Object element) { // 單一通用入口
        try {
            Method method = getClass().getMethod("visit" + element.getClass().getSimpleName(), element.getClass());
            method.invoke(this, element);
        } catch (NoSuchMethodException e) {
            defaultVisit(element); // 找不到對應方法時的預設處理
        } catch (Exception e) { throw new RuntimeException(e); }
    }
    
    // 保留具體 visitXxx 方法供反射調用
    public void visitString(String s) { System.out.println("'" + s + "'"); }
    public void visitInteger(Integer i) { System.out.println(i + "i"); }
    // ...
}

總結

訪問者模式是「資料結構穩定、操作多變」場景的終極解法。它以雙重分派實現編譯期類型安全的操作分發,將散落在各處的 instanceof 邏輯集中到訪問者類別中。雖然引入了介面與類別的複雜度,但換來的是極佳的擴展性與職責清晰度。現代 Java 結合反射或模式匹配,可進一步降低其剛性限制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved