在 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);
}
}
I want to know whether the below is an acceptable use of the visitor pattern. I feel a little uncomfortable returning from an Accept() or Visit() call - is this an appropriate usage of this pattern and if not, why not?
Note: Apologies for the long code sample, seems necessary to get across what I'm doing as visitor always seems to be a little involved...
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
对我来说,这感觉实施有点犹豫。
让您的 Visit 和 Accept 方法返回 void 并跟踪 Visitor 对象中的所有状态。 最后询问一下。
或者...
让访问和接受返回正在进行的状态并以功能方式接受传入的正在进行的状态。
如果您选择第二个选项,我不太确定是否需要访问者对象或模式,您可以使用迭代器、函数和一些瞬态状态。
Well to me this feels like the implementation is a little bit on the fence.
Either have your Visit and Accept methods return void and track all the state in the Visitor object. Interrogate it at the end.
or ...
Have Visit and Accept return an in-progress state and accept an incoming in-progress state in a functional manner.
If you go for the second option I'm not really sure that a visitor object or pattern is needed, you can use an iterator, function and some transient state instead.
简短回答:我没有发现公开 IVisitor 返回通用参数有任何问题。
请参阅 FxCop 规则。
然后它允许使用不同的IVisitor,每个都返回不同的值。
但是,就您的情况而言,Visitor 没有用,因为每种动物都有 Age 属性,因此所有操作都可以使用 Animal 或新的 <强>IAnimal界面。
另一种方法是使用多重调度,但代价是丢失强类型。
当您想要替换(或避免编写) 像这样的开关:
或者嵌套的if-then-else等价物。
其目的是通过使用多态性将实例路由到与其类型相关的例程,然后避免丑陋的 if-then-else/switch 语句和手动转换。 此外,它还有助于减少不相关代码之间的耦合。
替代方法是在类树中添加一个虚拟方法来访问。 然而,有时这是不可能或不可取的:
这就是为什么它经常被用来遍历对象树(html 节点、词法分析器标记等)。 Visitor 模式意味着以下接口:
IVisitor
IVisitable
每个IVisitable 中Accept 的实现应该调用Visit(this)。
Short answer: I don't see any problems of exposing a IVisitor returning a generic parameter.
See FxCop rules.
It then permits to use different IVisitor each returning a different value.
However, in your case, Visitor is not useful, since every animal has the Age property so all can be done with Animal or a new IAnimal interface.
Alternative is using multiple-dispatch at the cost of losing Strong Typing.
Use a Visitor pattern when you want to replace (or avoid to write) a switch like this one:
or the nested if-then-else equivalent.
It's purpose is to route the instance to the routine relevant for its type by using polymorphism and then avoid ugly if-then-else/switch statements and manual casts. Furthermore, it helps to decrease coupling between unrelated code.
Alternative to that is to add a virtual method in the class tree to visit. However, sometimes it's not possible or desirable :
That's why it's often used to traverse an object tree (html nodes, lexer tokens, etc...). Visitor pattern implies the following interfaces:
IVisitor
IVisitable
Implementation of Accept in each IVisitable should call Visit(this).
可访问的接受方法不应返回任何内容。 接受仅应指示访问者在访问之后或访问期间要访问什么。
The visitable accept method is not supposed to return anything. The accept is only supposed to indicate the visitor what to visit after or during the visit.
这很常见。 我不知道你是否可以在 C# 中做到这一点,但在 Java 中,通常将 Accept 方法保留为通用,因此返回的内容由访问者而不是访问者决定:
对于过程,一个null 的 code> 。
IAnimalVisitor
IAnimalVisitorIt's fairly common. I don't know if you can do it in C#, but in Java it's normal to leave the Accept method generic, so what's returned is decided by the visitor not the visitee:
For procedures, a
IAnimalVisitor<Void>
returningnull
can be used.