2012-12-17 86 views
2

我做得很好主人訪問者模式。 但是,我想知道一些事情。實現使用實例的訪問者

使用訪問者模式最重要的動機是在客戶端添加涉及特定數據模型的邏輯,而不需要檢查真實的數據對象類型。用於求解的技術被稱爲雙調度(Double-Dispatching)。

所以,這裏的數據模型的代碼片段實現的accept()方法:

public class Ferrari extends Car { 
    //...... 
    @Override 
    public void accept(Visitor v){ 
     v.visit(this); 
    } 
    } 

而且這裏PrintCarVisitor實現Visitor接口:

public class PrintCarVisitor implements Visitor { 
    //... 
    @Override 
    public void visit(Ferrari c){ 
    System.out.println("Ferrari"); 
    } 
} 

因此,沒有if/else系列和instanceof系列需要。

任何客戶端將是:

Visitor visitor = new PrintCarVisitor(); 
car.accept(visitor); //no need to know the exact Car's type 

然而,由於遊客不守開/閉原則(因爲新的數據模型導致通過增加自己的visit方法打破類),爲什麼我們打擾雙重派遣?

難道我們不能僅僅在訪客實現中隔離if/else系列。

有了這個假設的替代,這部分代碼就會消失:

public class Ferrari extends Car { 
    //This method is not needed anymore with this alternative 
    @Override 
    public void accept(Visitor v){ 
     v.visit(this); 
    } 
    } 

PrintCarVisitor將是:

public class PrintCarVisitor { 
    public void visit(Car c){ 
    if(c instanceof Ferrari){ 
     System.out.println("Ferrari"); 
    } 
    }  
} 

有了這個選擇,每一個來電者仍然會處理這樣的數據模型抽象:

new PrintCarVisitor().visit(car); //no need to know the exact data type in client side

先驗,第二種方法非常好,因爲它不涉及實現純訪問者模式期間生成的所有樣板文件。

我想像這種方法有兩個缺點:

1)有沒有保證(如Visitor接口強行),任何訪問者使用對應於當前處理的Car的方法處置。

2)BoilerPlate代碼在遊客實現類中仍然較重,其中instanceofcasting系列爲。

他們是否存在其他任何缺陷,解釋爲什麼訪客模式必須使用Double Dispatching,因此不能簡單地在類中隔離instanceof系列(例如靜態Factory)?

回答

4
  1. 如果你這樣做,你不再有一個訪客,你基本上有某種處理器。你的代碼只是一個列表循環,每次循環時,你都會將以前使用的accept傳遞給處理器,這正是訪問者。而不是訪問訪問者,你在某種意義上顛倒了範式;訪問者成爲訪問者,因爲它最初傳遞給工作人員。你可以做到這一點;儘管如此,你不會稱之爲訪客。

  2. 傳統的看法通常決定使用instanceof應該爲最後手段保留。爲什麼你會使用instanceof,當你可以讓Java的多態性爲你做?擁有物體的一個要點就是這個好處。如果你這樣做了,爲什麼不避開覆蓋方法,而只是使用instanceof來確定在類的方法中要做什麼,而不是依賴動態調度,以覆蓋方法的情況?

+0

我喜歡你的答案。我完全同意'instanceof'不是OO,所以應該避免。我的問題不是爲了瞭解最佳做法,而是強烈理解爲什麼雙重調度是訪客模式的唯一選擇。謝謝;) – Mik378

1

Xtext項目有同樣的問題,他們創建了一個幫助類PolymorphicDispatcher。簡而言之,PolymorphicDispatcher會在運行時在編譯時執行編譯器所執行的操作:找到最適合匹配一組參數的方法並調用它。

所以你的訪問者會是這樣的:

public class PrintCarVisitor { 
    public void visit(Car c){ 
     System.out.println("Just a car"); 
    } 
    public void visit(Ferrari c){ 
     System.out.println("A Ferrari!"); 
    } 
} 

Here is the source.

+0

但是,編譯時檢查的缺乏只發生在我上面描述的instanceof替代方法中。那麼,爲什麼在實現訪問者時「安全」涉及「未知」汽車的運行時調用...... – Mik378

+0

因爲它使得代碼可以重複使用並且可以在不改變'Car'或其他訪問者的代碼的情況下擴展。 'Car'可以在'accept()'方法中使用'PolymorphicDispatcher'來爲實際運行時類型調用通用或特定的方法。 –

+0

我想我明白了:使用這個解決方案(使用反射機制),不需要在數據模型中聲明一個'accept()'方法。這樣做會阻止編譯時類型檢查,但是由於反射,運行時添加了類似的類型檢查功能。 – Mik378