在C#中实现访问者模式

moo*_*dle 7 c# visitor-pattern

我是这种模式的新手,可以请别人帮助我吗?

我有一个像这样的对象:

public class Object
    {
        public string Name { get; set; }
        public object Value { get; set; }
        public List<Object> Childs { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

这是一个JSON示例:

  {
    "Name": "Method",
    "Value": "And",
    "Childs": [{
        "Name": "Method",
        "Value": "And",
        "Childs": [{
            "Name": "Operator",
            "Value": "IsEqual",
            "Childs": [{
                "Name": "Name",
                "Value": "5",
                "Childs": []
            }]
        },
        {
            "Name": "Operator",
            "Value": "IsEqual",
            "Childs": [{
                "Name": "Name",
                "Value": "6",
                "Childs": []
            }]
        }]
    },
    {
        "Name": "Operator",
        "Value": "IsEqual",
        "Childs": [{
            "Name": "Name",
            "Value": "3",
            "Childs": []
        }]
    }]
}
Run Code Online (Sandbox Code Playgroud)

我的问题如何使访问者模式,以获得这个最后的字符串:

(Name IsEqual 3)And((Name IsEqul 5)And(Name IsEqual 6))
Run Code Online (Sandbox Code Playgroud)

Meh*_*taş 20

要实现访问者模式,您需要两个简单的接口

  1. IVisitable使用Accept具有IVisitor参数的方法.
  2. IVisitorVisit每个实现的方法都很多IVisitable

因此访问者模式的基本思想是根据实现的类型动态地改变行为.

对于你的情况,你想要访问的东西(可访问的)是Object显然没有不同衍生物的类,你想根据属性值而不是类型来改变行为.所以访客模式不是你真正需要的,我强烈建议你用递归方法来考虑答案.

但是如果你真的想在这里使用访问者模式,它可能看起来像这样.

interface IVisitable { void Accept(IVisitor visitor); }

interface IVisitor {
    void VisitAnd(Object obj);
    void VisitEquals(Object obj);
}
Run Code Online (Sandbox Code Playgroud)

由于Object该类是一个简单的POCO,我假设你不想实现一个接口并在这个类中添加一个方法.所以你需要一个适配器,该适配对象Object,以IVisitable

class VisitableObject : IVisitable {
    private Object _obj;

    public VisitableObject(Object obj) { _obj = obj; }

    public void Accept(IVisitor visitor) {
        // These ugly if-else are sign that visitor pattern is not right for your model or you need to revise your model.
        if (_obj.Name == "Method" && _obj.Value == "And") {
            visitor.VisitAnd(obj);
        }
        else if (_obj.Name == "Method" && _obj.Value == "IsEqual") {
            visitor.VisitEquals(obj);
        }
        else
            throw new NotSupportedException();
        }
    }
}

public static ObjectExt {
    public static IVisitable AsVisitable(this Object obj) {
        return new VisitableObject(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,访问者的实现可能看起来像这样

class ObjectVisitor : IVisitor {
    private StringBuilder sb = new StringBuilder();

    public void VisitAnd(Object obj) {
        sb.Append("(");
        var and = "";
        foreach (var child in obj.Children) {
            sb.Append(and);
            child.AsVisitable().Accept(this);
            and = "and";
        }
        sb.Append(")");
    }

    public void VisitEquals(Object obj) {
        // Assuming equal object must have exactly one child 
        // Which again is a sign that visitor pattern is not bla bla...
        sb.Append("(")
          .Append(obj.Children[0].Name);
          .Append(" Equals ");
          .Append(obj.Children[0].Value);
          .Append(")");
    }
}
Run Code Online (Sandbox Code Playgroud)


Fab*_*Fab 8

JSON清楚地表示令牌树(可能由解析器生成)。

访客模式使用多态。

为了被访问者模式使用,必须对它进行反序列化以获得具有不同访问行为的对象:

  • 方法令牌
  • OperatorToken
  • 名称令牌

然后,IVisitor应该为每个对象实现Visit方法:

public interface IVisitor
{
    void Visit(MethodToken token) { /* */ }
    void Visit(OperatorToken token) { /* */ }
    void Visit(NameToken token) { /* */ }
}

public interface IVisitable
{
    void Accept(IVisitor visitor);
}

public class MethodToken : IVisitable
{
    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

附加说明:

Object是一个非常糟糕的名字,尤其是在C#中Object,每个类的基类都是这样,更不用说冲突了,它没有传达任何特殊含义……令牌呢?

public class Token
{
    public string Name { get; set; }
    public object Value { get; set; }
    public List<Token> Childs { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

关于物业儿童房

访客目的

如果您不知道何时/为什么使用螺丝刀,则不要使用螺丝刀(这样会很危险)。

访客模式非常有用,可以避免“难看” /难以维护/痛苦地阅读许多开关情况,甚至更糟,if else if else同时还为您提供了强大的类型检查优势。它还有助于将相关代码(高内聚性)保持在一个类(Visitor)中。当然,一旦实现,对象树(此处为令牌)就可以被多种类型的访问者访问,只要它们实现了IVisitor接口即可。

在您的情况下,您必须首先将它们转换Token为的强子类型 Token(通过字典映射,以避免任何if / switch或自定义反序列化)