在C#中使用带有generics的访问者模式

我想知道以下是否是访客模式的可接受用途。 从Accept()或Visit()调用返回时我感到有点不舒服 – 这是否适合使用此模式,如果没有,为什么不呢?

注意:对于长代码示例的道歉,似乎有必要了解我正在做的事情,因为访问者似乎总是有点参与……

interface IAnimalElement { T Accept(IAnimalVisitor visitor); } interface IAnimalVisitor { T Visit(Lion lion); T Visit(Peacock peacock); T VisitZoo(List animals); } abstract class Animal { public int Age { get; protected set; } } class Lion : Animal, IAnimalElement { public Lion(int age) { Age = age; } public int Accept(IAnimalVisitor visitor) { return visitor.Visit(this); } } class Peacock : Animal, IAnimalElement { public Peacock(int age) { Age = age; } public int Accept(IAnimalVisitor visitor) { return visitor.Visit(this); } } class AnimalAgeVisitor : IAnimalVisitor { public int TotalAge { get; private set; } int IAnimalVisitor.Visit(Lion lion) { TotalAge += lion.Age; return lion.Age; } int IAnimalVisitor.Visit(Peacock peacock) { TotalAge += peacock.Age + 10; return peacock.Age + 10; // peacocks ages are always -10y, correct. } public int VisitZoo(List animals) { // Calculate average animal age. int sum = 0; int count = 0; foreach (IAnimalElement animal in animals) { sum += animal.Accept(this); ++count; } return count == 0 ? 0 : sum / count; } } class Program { static void Main(string[] args) { List animals = new List() { 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对象中的所有状态。 最后询问它。

要么 …

访问和接受返回正在进行的状态并以function方式接受进入的进行中状态。

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

这很常见。 我不知道你是否可以在C#中做到这一点,但是在Java中保留Accept方法是通用的是正常的,所以返回的是由访问者而不是被访者决定的:

 interface IAnimalElement {  T Accept(IAnimalVisitor visitor); } interface IAnimalVisitor { T Visit(Peacock animal); ... } 

对于过程,可以使用返回nullIAnimalVisitor

可访问的accept方法不应返回任何内容。 接受仅应指示访客在访问之后或访问期间访问的内容。

简短回答:我没有看到暴露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

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

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

在每个IVisitable中实现Accept应该调用Visit(this)