[英]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);
...
}
对于过程,可以使用返回null
的IAnimalVisitor<Void>
。
可访问的accept方法不应返回任何内容。 接受仅应指示访客在访问之后或访问期间访问的内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.