簡體   English   中英

在C#中使用帶有泛型的訪問者模式

[英]Using the visitor pattern with generics in C#

我想知道以下是否是訪客模式的可接受用途。 從Accept()或Visit()調用返回時我感到有點不舒服 - 這是否適合使用此模式,如果沒有,為什么不呢?

注意:長代碼示例的道歉,似乎有必要了解我正在做的事情,因為訪問者似乎總是有點參與......

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);
    }
}

對我來說,這感覺就像實施有點障礙。

讓您的Visit和Accept方法返回void並跟蹤Visitor對象中的所有狀態。 最后詢問它。

要么 ...

訪問和接受返回正在進行的狀態,並以功能方式接受進入的進行中狀態。

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

簡短回答:我沒有看到暴露IVisitor返回泛型參數的任何問題。
請參閱FxCop規則

然后允許使用不同的IVisitor,每個IVisitor返回不同的值。

但是, 在您的情況下 ,訪問者沒有用,因為每個動物都有Age屬性,因此所有動物都可以使用Animal或新的IAnimal界面完成。

替代方案是使用多次發送 ,但代價是失去強類型

如果要替換(或避免寫入)像這樣的開關,請使用訪問者模式

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等價物。

它的目的是通過使用多態來將實例路由到與其類型相關的例程,然后避免丑陋的if-then-else / switch語句手動強制轉換 此外,它有助於減少不相關代碼之間的耦合

替代方法是在類樹中添加一個虛擬方法來訪問。 但是,有時它不可能或不可取:

  • 可訪問的類代碼不可修改 (例如,不擁有)
  • 與訪問代碼無關的可訪問類代碼 (在類中添加它將意味着降低類的內聚力 )。

這就是為什么它經常用於遍歷對象樹(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中實現Accept應該調用Visit(this)

這很常見。 我不知道你是否可以在C#中做到這一點,但在Java中,保留Accept方法是通用的是正常的,所以返回的內容由訪問者而不是被訪問者決定:

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


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

對於過程,可以使用返回nullIAnimalVisitor<Void>

可訪問的accept方法不應返回任何內容。 接受僅應指示訪客在訪問之后或訪問期間訪問的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM