8

我想知道下面的访问者模式是否可以接受。从 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);
    }
}
4

4 回答 4

4

对我来说,这感觉就像实施有点悬而未决。

要么让您的 Visit 和 Accept 方法返回 void 并跟踪 Visitor 对象中的所有状态。最后询问一下。

或者 ...

让访问和接受返回进行中状态并以功能方式接受传入的进行中状态。

如果您选择第二个选项,我不确定是否需要访问者对象或模式,您可以使用迭代器、函数和一些瞬态代替。

于 2009-02-04T05:09:47.240 回答
3

简短的回答:我没有看到公开返回泛型参数的 IVisitor 的任何问题。
请参阅FxCop 规则

然后它允许使用不同的IVisitor ,每个返回不同的值。

但是,在您的情况下, Visitor 没有用,因为每只动物都有Age属性,所以一切都可以通过Animal或新的IAnimal接口来完成。

另一种选择是以丢失Strong Typing为代价使用多次调度

当您想替换(或避免编写)这样的开关时,请使用访问者模式

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 节点、词法分析器标记等)。访问者模式意味着以下接口:

  • 访问者

    /// <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{}
    
  • 可访问

    /// <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 {}
    

每个IVisitableAccept的实现应该调用Visit(this)

于 2015-12-23T18:23:38.507 回答
2

可访问的接受方法不应该返回任何东西。接受仅应指示访问者在访问之后或访问期间访问什么。

于 2011-05-02T18:38:02.767 回答
1

这是相当普遍的。我不知道您是否可以在 C# 中执行此操作,但在 Java 中,将 Accept 方法保留为通用方法是正常的,因此返回的内容由访问者而不是访问者决定:

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


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

对于程序,可以使用IAnimalVisitor<Void>返回。null

于 2009-02-03T18:51:44.117 回答