16

I have a hierarchy of classes that represents GUI controls. Something like this:

Control->ContainerControl->Form

I have to implement a series of algoritms that work with objects doing various stuff and I'm thinking that Visitor pattern would be the cleanest solution. Let take for example an algorithm which creates a Xml representaion of a hierarchy of objects. Using 'classic' approach I would do this:

public abstract class Control
{
    public virtual XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = document.CreateElement(this.GetType().Name);
        // Create element, fill it with attributes declared with control
        return xml;
    }
}

public abstract class ContainerControl : Control
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Use forech to fill XmlElement with child XmlElements
        return xml;
    }
}

public class Form : ContainerControl
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Fill remaining elements declared in Form class
        return xml;
    }
}

But I'm not sure how to do this with visitor pattern. This is the basic implementation:

public class ToXmlVisitor : IVisitor
{
    public void Visit(Form form)
    {
    }
}

Since even the abstract classes help with implementation I'm not sure how to do that properly in ToXmlVisitor?

The reason that I'm considering Visitor pattern is that some algorithms will need references not available in project where the classes are implemented and there is a number of different algorithms so I'm avoiding large classes.

  • what is your question? – gnat Oct 23 '13 at 13:52
  • Basically how to rewrite ToXml() method using a visitor pattern. – Nezreli Oct 23 '13 at 14:37
  • Thanks for the link. The dynamic dispatch simplifies traditional visitor pattern but it doesn't change much. – Nezreli Oct 23 '13 at 14:59
  • @Nezreli Yes it does. It works with classes that don't support the Visitor pattern, such as the Windows Forms controls you are dealing with. – Kris Vandermotten Oct 23 '13 at 15:02
17

The visitor pattern is a mechanism to simulate dual binding in programming languages that only support single binding. Unfortunately, that statement might not clarify things a lot, so let me explain with a simple example.

In .NET and C#, the platform you are using, objects can be converted to strings using the ToString() function. What that funtion does, i.e. the code being executed, depends on the type of object you're applying it to (it's a virtual method). What code is executed depends on one thing, the one type of the object, hence the mechanism used is called single binding.

But what if I want to have more than one way to convert an object to a string, for each different kind of object? What if I wanted to have two ways to convert objects to strings, so that the code being executed depends on two things: not only the object to be converted, but also the way in which we want it to be converted?

That could be solved nicely if we had dual binding. But most OO languages, including C#, only support single binding.

The visitor pattern solves the problem, by turning dual binding into two succesive single bindings.

In our example above, it would use a virtual method in the object to convert, that calls a second virtual method in the object implementing the conversion algorithm.

But that implies that the object upon which you want to apply the algorithm needs to collaborate with this: it needs to have support for the visitor pattern baked in.

You seem to be using .NET's Windows Forms classes, which do not have support for the visitor pattern. More specifically, they would need to have a public virtual void Accept(IVisitor) method, which obviously they don't have.

So, what's the alternative? Well, .NET does not only support single binding, it also supports dynamic binding, which is even more peowerful than dual binding.

For more information on how to apply that technique, which will allow you to solve your problem (if I understand it well), take a look at Farewell Visitor.

UPDATE:

To apply the technique to your specific problem, first define your extension method:

public static XmlDocument ToXml(this Control control)
{
    XmlDocument xml = new XmlDocument();
    XmlElement root = xml.CreateElement(control.GetType().Name);
    xml.AppendChild(root);

    Visit(control, xml, root);

    return xml;
}

Create the dynamic dispatcher:

private static void Visit(Control control, XmlDocument xml, XmlElement root)
{
    dynamic dynamicControl = control; //notice the 'dynamic' type.
                                      //this is the key to dynamic dispatch

    VisitCore(dynamicControl, xml, root);
}

Then fill in the specific methods:

private static void VisitCore(Control control, XmlDocument xml, XmlElement root)
{
    // TODO: specific Control handling
}

private static void VisitCore(ContainerControl control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as Control, xml, root);

    // TODO: specific ContainerControl handling
    // for example:
    foreach (Control child in control.Controls)
    {
        XmlElement element = xml.CreateElement(child.GetType().Name);
        root.AppendChild(element);

        // call the dynamic dispatcher method
        Visit(child, xml, element);
    }
}

private static void VisitCore(Form control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as ContainerControl, xml, root);

    // TODO: specific Form handling
}
  • dynamic dispatch in .NET is indeed quite powerfull .. however I noticed it could be a bit... well.. slow, but it does in a single line of code what takes sevral lines in multiple classes and interfaces with a visitor – Newtopian Oct 23 '13 at 23:33
  • Still, dynamic dispatch won't solve my problem because my ToXml algorithm requires me to 'visit' all types up the inheritance chain. In my example it needs to visit Control, ContainterControl and Form in that order to have a successful XML conversion. – Nezreli Oct 24 '13 at 7:51
  • @Nezreli It can solve your problem, I've updated my answer to show you how. – Kris Vandermotten Oct 24 '13 at 8:39
  • I took the permission to add a comment to the dynamic variable definition. It took me two reads of the code before I spotted it, and it's the key to the whole story. – Cristi Diaconescu Mar 30 '15 at 9:31

protected by gnat Jan 18 '15 at 12:35

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.