Search code examples
c#visitor-pattern

Implementing visitor Pattern in C#


I'm new in this pattern , could please someone help me in it?

I got an Object like this :

public class Object
    {
        public string Name { get; set; }
        public object Value { get; set; }
        public List<Object> Childs { get; set; }
    }

Here is an JSON example:

  {
    "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": []
        }]
    }]
}

My question how to make Visitor Pattern in order to get this final string:

(Name IsEqual 3)And((Name IsEqul 5)And(Name IsEqual 6))

Solution

  • To implement visitor pattern you need two simple interfaces

    1. IVisitable with an Accept method having the IVisitor as the parameter.
    2. IVisitor with many Visit methods for each implementation of IVisitable

    So basic idea of the visitor pattern is to change the behavior dynamically according to the type of implementation.

    For your case the thing you want to visit (the visitable) is the Object class which apparently does not have different derivatives and you want to change the behavior according to a property value not the type. So Visitor Pattern is not what you really need here and I highly recommend you to consider the answers with the recursive method.

    But if you really want to use visitor pattern here, it may look something like this.

    interface IVisitable { void Accept(IVisitor visitor); }
    
    interface IVisitor {
        void VisitAnd(Object obj);
        void VisitEquals(Object obj);
    }
    

    Since the Object class is a simple POCO I assume you won't want to implement an interface and add a method into this class. So you'll need an adapter object which adapts Object to 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);
        }
    }
    

    And finally the visitor implementation may look like this

    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(")");
        }
    }