如何实现访问者模式以生成 XML?

Pet*_*ris 2 c# java design-patterns visitor-pattern

我有一个包含许多类的复合类结构。由于许多不同的原因(验证、克隆、导出为 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; }
}
Run Code Online (Sandbox Code Playgroud)

如果我想生成这样的 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>
Run Code Online (Sandbox Code Playgroud)

首先让我担心的是,通常我会有 VisitOwner 和 VisitOwned,这可以很好地进行验证,但在 XML 中,我需要将拥有的对象包装在它们各自的 likeThings 或 dislikedThings XML 节点中。

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

das*_*ght 5

The Visitor Pattern lets you build the structure for the double-dispatch side of the problem, helping you deal with the complexity that is due to your inheritance structure of your model. However, the classical form of the pattern does nothing to deal with the complexity that is due to the compositional structure of your model, especially when the same class is used multiple times in different capacities.

In your case, a solution must work out both of these complexities - on one hand, you have the Owner vs. the Owned; on the other side, you have the Liked, Disliked, and whatever else that you plan to add.

The task of dealing with the composition side is traditionally given to implementations of the visitor, not to the interface. However, the compiler would not be able to help you find violators that fail to process the newly relationships. However, you can combine the visitor pattern with the Template Method Pattern to create a hybrid solution that deals with both problems.

Here is a skeleton of what you can do:

// 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();
}
Run Code Online (Sandbox Code Playgroud)

The advantage of structuring your code like that is that the compiler is now on the hook for checking implementations of DefaultVisitor for presence of all abstract methods; the disadvantage is that these implementations will have to provide eight implementations instead of two.