8 June 2005

A truly decoupled visitor

From the Design Patterns [Amazon] book to Modern C++ Design [Amazon], I've been frustrated that the technique I use to implement the visitor pattern is not offered or even refuted. I'm either on to something or, quite the opposite, off of something. Or maybe simply on something.

The visitor pattern describes how to act upon collections of objects in a generic manner. Collection elements contain a single function that accept a visitor class and pass themselves to that class when called. Visitor classes accept elements and act upon them depending on their type. A separate function (usually in the owner of the collection) performs the iteration.

// Have the document expand each element by 1 pica
Document::Expand()
{

    ExpandVisitor v;
    for each element
    {
        element.Accept(v);
    }

}

If an element cannot accept the visitor (e.g. perhaps a watermark would not be expanded), the visitor skips it. This pattern requires alteration of the elements themselves, so even with Alexandrescu's elegant and generic template approach it's a little intrusive.

I still use the variation I came up with in my first implementation of the visitor pattern, and although programmers often fail to unlearn bad habits, I feel that it's a more useable solution. Because I generally am not able to edit either the elements or the owner of the collection, I create a traversal class that accepts visitor-derived classes. The traversal class performs the element iteration, and passes each element to the visitor class.

// Construct with a reference to the container.
Traversal::Traversal(Container & c)

Traversal::Process(Visitor & v)
{

    for each element in c
    {
        v.Visit(element);
    }

}

New actions are created (as with the standard implementation) by deriving from the base visitor class. The traversal class, once written, is static.

[ posted by sstrader on 8 June 2005 at 4:09:47 PM in Programming ]