2009-02-03 60 views
7

我想知道下面是否可以使用訪問者模式。從Accept()或Visit()調用返回時,我感到有點不舒服 - 這是這種模式的適當用法,如果不是,爲什麼不呢?在C中使用泛型的訪問者模式#

注:長的代碼示例道歉,似乎有必要跨越我在做什麼,以獲得儘可能遊客似乎總是有點棘手...

interface IAnimalElement<T> 
{ 
    T Accept(IAnimalVisitor<T> visitor); 
} 

interface IAnimalVisitor<T> 
{ 
    T Visit(Lion lion); 
    T Visit(Peacock peacock); 
    T VisitZoo(List<Animal> animals); 
} 

abstract class Animal 
{ 
    public int Age { get; protected set; } 
} 

class Lion : Animal, IAnimalElement<int> 
{ 
    public Lion(int age) 
    { 
     Age = age; 
    } 

    public int Accept(IAnimalVisitor<int> visitor) 
    { 
     return visitor.Visit(this); 
    } 
} 

class Peacock : Animal, IAnimalElement<int> 
{ 
    public Peacock(int age) 
    { 
     Age = age; 
    } 

    public int Accept(IAnimalVisitor<int> visitor) 
    { 
     return visitor.Visit(this); 
    } 
} 

class AnimalAgeVisitor : IAnimalVisitor<int> 
{ 
    public int TotalAge { get; private set; } 

    int IAnimalVisitor<int>.Visit(Lion lion) 
    { 
     TotalAge += lion.Age; 
     return lion.Age; 
    } 

    int IAnimalVisitor<int>.Visit(Peacock peacock) 
    { 
     TotalAge += peacock.Age + 10; 
     return peacock.Age + 10; // peacocks ages are always -10y, correct. 
    } 

    public int VisitZoo(List<Animal> animals) 
    { 
     // Calculate average animal age. 

     int sum = 0; 
     int count = 0; 
     foreach (IAnimalElement<int> animal in animals) 
     { 
      sum += animal.Accept(this); 
      ++count; 
     } 

     return count == 0 ? 0 : sum/count; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Animal> animals = new List<Animal>() { new Lion(10), 
      new Lion(15), new Peacock(3), new Lion(2), new Peacock(9) }; 

     AnimalAgeVisitor visitor = new AnimalAgeVisitor(); 

     Console.WriteLine("Average age = {0}, Total age = {1}", 
      visitor.VisitZoo(animals), visitor.TotalAge); 
    } 
} 
+0

哦,順便說一句,你應該完全使用`[DebuggerStepThrough]``屬性在'Accept'方法上。 – 2014-02-07 09:58:02

回答

3

對我來說,這感覺就像是圍牆上的實現。

要麼您的訪問和接受方法返回void並跟蹤Visitor對象中的所有狀態。在最後詢問它。

或...

有無訪問,並接受以功能性方式返回正在進行的狀態,並接受傳入正在進行的狀態。

如果您選擇第二個選項,我不太確定是否需要訪問者對象或模式,您可以使用迭代器,函數和一些臨時狀態。

1

這是相當普遍的。我不知道你是否能在C#中做到這一點,但在Java這是正常離開accept方法通用的,所以所返回的是什麼訪問者不是visitee決定:

interface IAnimalElement 
{ 
    <T> T Accept(IAnimalVisitor<T> visitor); 
} 


interface IAnimalVisitor<T> 
{ 
    T Visit(Peacock animal); 
    ... 
} 

有關過程,一個IAnimalVisitor<Void>返回可以使用null

1

可視化接受方法不應該返回任何東西。接受只是表明訪問者在訪問之後或訪問期間要訪問的內容。

1

簡短回答:我沒有看到任何暴露IVisitor的問題返回一個通用的參數。
FxCop rules

然後它允許使用不同的IVisitor每個返回一個不同的值。

然而,你的情況,觀衆是沒有用的,因爲每一種動物有年齡屬性,因此都可以用動物或新IAnimal界面來完成。

方法是使用multiple-dispatch在失去強類型的成本。

使用Visitor pattern當你想更換(或避免寫)一開關像這樣的:IF-THEN-ELSE相當於

IAnimal animal = ...; 
switch (animal.GetType().Name) 
{ 
    case "Peacock": 
    var peacock = animal as Peacock; 
    // Do something using the specific methods/properties of Peacock 
    break; 
    case "Lion": 
    var peacock = animal as Lion; 
    // Do something using the specific methods/properties of Lion 
    break; 
    etc... 
} 

或嵌套

它的目的是爲了路由實例的日常相關的類型,通過使用多態,然後避免醜陋的if-then-else的/ switch語句手動施放。此外,它有助於減少耦合之間無關的代碼。

另一種方法是在類樹中添加一個虛擬方法來訪問。但是,有時這是不可能的或希望的:

  • 的Visitable類代碼不可修改(不屬於爲例)
  • 的Visitable類代碼不相關的訪問代碼(在類中添加這將意味着降低凝聚力)。

這就是爲什麼它經常用來遍歷對象樹(html節點,詞法分析器等等)。 訪問者圖案意味着以下接口:

  • IVisitor

    /// <summary> 
    /// Interface to implement for classes visiting others. 
    /// See Visitor design pattern for more details. 
    /// </summary> 
    /// <typeparam name="TVisited">The type of the visited.</typeparam> 
    /// <typeparam name="TResult">The type of the result.</typeparam> 
    public interface IVisitor<TVisited, TResult> : IVisitor where TVisited : IVisitable 
    { 
        TResult Visit(TVisited visited); 
    } 
    
    /// <summary> 
    /// Marking interface. 
    /// </summary> 
    public interface IVisitor{} 
    
  • IVisitable

    /// <summary> 
    /// Interface to implement for classes visitable by a visitor. 
    /// See Visitor design pattern for more details. 
    /// </summary> 
    /// <typeparam name="TVisitor">The type of the visitor.</typeparam> 
    /// <typeparam name="TResult">The type of the result.</typeparam> 
    public interface IVisitable<TVisitor, TResult> : IVisitable where TVisitor : IVisitor 
    { 
        TResult Accept(TVisitor visitor); 
    } 
    
    /// <summary> 
    /// Marking interface. 
    /// </summary> 
    public interface IVisitable {} 
    

執行接受在每個IVisitable應調用訪問(本)