2

我有一个包含许多类的复合类结构。由于许多不同的原因(验证、克隆、导出为 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 对象包装在它们各自的 likeThings 或 dislikedThings XML 节点中。

我关心的第二件事是,对于尚未为组合的任何新部分(例如,新属性“List<Owned> SmellyThings”)实现操作的每个现有访问者实现,我想要一个编译时错误

4

1 回答 1

5

访问者模式让您可以为问题的双重调度方面构建结构,帮助您处理由于模型的继承结构而导致的复杂性。但是,模式的经典形式无法处理由于模型的组合结构而导致的复杂性,尤其是当同一个类以不同的能力多次使用时。

在您的情况下,解决方案必须解决这两个复杂性 - 一方面,您有OwnerOwned; 另一方面,您有Liked,Disliked以及您计划添加的任何其他内容。

处理组合方面的任务传统上交给访问者的实现,而不是接口。但是,编译器将无法帮助您找到未能处理新关系的违规者。但是,您可以将访问者模式与模板方法模式结合起来创建一个混合解决方案来处理这两个问题。

这是您可以执行的操作的概要:

// 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所有抽象方法的实现;缺点是这些实现必须提供八个实现而不是两个。

于 2013-11-06T20:10:37.550 回答