我有一个复合类结构,涉及许多类。需要遍历此结构有许多不同的原因(验证,克隆,导出为xml等),因此编写使用访问者模式是有意义的。给出以下类结构
class Owner
{
public string Name { get; set; }
public List<Owned> Liked { get; private set; }
public List<Owned> Disliked { get; private set; }
public Owner()
{
this.Liked = new List<Owned>();
this.Disliked = new List<Owned>();
}
}
class Owned
{
public string Name { get; set; }
}
如果我想像这样生成XML
,应该如何实现这样的访问者模式<owner>
<name>Owner 1</name>
<likedThings>
<owned>
<name>Liked thing 1</name>
</owned>
<owned>
<name>Liked thing 2</name>
</owned>
</likedThings>
<dislikedThings>
<owned>
<name>Disliked thing 1</name>
</owned>
<owned>
<name>Disliked thing 2</name>
</owned>
</dislikedThings>
</owner>
我首先关注的是通常我会使用VisitOwner和VisitOwned,这样可以很好地进行验证,但在XML中我需要将Owned对象包装在各自的likesThings或dislikedThings XML节点中。
我担心的第二件事是,我希望每个现有的访问者实现都有一个编译时错误,该实现尚未对组合的任何新部分实施操作(例如,新属性“List&lt; Owned&gt; SmellyThings”)
答案 0 :(得分:4)
Visitor Pattern允许您为问题的双重调度方构建结构,帮助您处理由模型的继承结构引起的复杂性。但是,模式的经典形式无法解决由于模型的组合结构而导致的复杂性,特别是当同一个类在不同的容量中多次使用时。
在您的情况下,解决方案必须解决这两个复杂问题 - 一方面,您拥有Owner
与Owned
;另一方面,您有Liked
,Disliked
以及您计划添加的任何其他内容。
处理组合方面的任务传统上是给访问者的实现,而不是接口。但是,编译器无法帮助您找到无法处理新关系的违规者。但是,您可以将访问者模式与Template Method Pattern结合使用,以创建一个处理这两个问题的混合解决方案。
以下是您可以执行的操作的框架:
// This is a run-of-the-mill visitor
interface IVisitor {
void VisitOwner(Owner owner);
void VisitOwned(Owned owned);
}
// This is a base visitor class; it is abstract
abstract class DefaultVisitor : IVisitor {
public void VisitOwner(Owner owner) {
BeginOwner(owner);
BeginLiked();
foreach (var owned in owner.Liked) {
owned.Accept(this);
}
EndLiked();
BeginDisliked();
foreach (var owned in owner.Disliked) {
owned.Accept(this);
}
EndDisliked();
EndOwner(owner);
}
public void VisitOwned(Owned owned) {
BeginOwned(owned);
EndOwned(owned);
}
public abstract void BeginOwner(Owner owner);
public abstract void EndOwner(Owner owner);
public abstract void BeginOwned(Owned owned);
public abstract void EndOwned(Owned owned);
public abstract void BeginLiked();
public abstract void EndLiked();
public abstract void BeginDisliked();
public abstract void EndDisliked();
}
构造代码的优点是编译器现在可以检查DefaultVisitor
的实现是否存在所有抽象方法;缺点是这些实现必须提供八个实现而不是两个。