Visitor Pattern

Jim's Pages => Java Pages => Visitor Pattern

The Visitor Pattern is found in the popular Design Patterns book, or here at Oberlin.

07-20-2003 About a year after writing this page, I came across another discussion of Visitor that focused on adding functionality to an object without modifying the object. That's a good way to look at it, too. Visitor is in fact a "double dispatch" technique that could be used on a single object as well as a collection. But even GoF starts off talking about iterating a collection, so I'm letting the rest of the page stand. 

In this pattern, we need to perform some operation on every object in some structure or collection of objects. Rather than figure out how to walk the tree or iterate the objects for every new operation we want to perform, we build the iterator into the structure.  We pass the "visitor" that does the operation to the structure, and the structure causes the operation to "visit" each object inside. This seems appropriate if you want to allow unforeseen operations on a structure that cannot be modified, e.g. a vendor-provided object graph.

Visitor is easier to illustrate in code than it is to describe. To get things rolling, we tell the root of the structure to accept a visitor:

root.accept( visitor )

The root and every child object in the structure do something like this:

function accept( visitor )
   visitor.visit( this );
   for each child of mine
      child.accept( visitor )
   next
end function

In short order our visitor gets a visit() call from each object in the collection.  

An interesting collection might contain different types of objects.  To handle this, the visitor overloads the method visit() with a variety of argument types. For example, the visitor interface for an HTML tool has methods like these:

visit( Document node )
visit( Tag node )
visit( TagBlock node )
visit( Comment node )
visit( Text node ) 

If a Document object calls visit(this), the first method is invoked. If a Comment object calls visit(this), the fourth method is invoked.

In Real Life

I ran across this pattern in the Quiotix html-parser. The Quiotix parser parses HTML into a tree of objects, not unlike the XML DOM. Once the parsing is done, I want to process all the nodes in the DOM. I could walk all the relationships in the DOM from parent to child. Maybe you've done this with XML. It is painful. And I wanted to make several custom tools that processed all the nodes. I might be tempted to copy this tree-walker logic into each one. Yuck.

With the visitor pattern - which Quiotix thoughtfully built into the DOM - I just pass a visitor to the DOM and the DOM does all the walking. I don't even have to know the details of the DOM structure.

I wrote several visitors to load a database of links to and from my pages, build a table of contents for each page from the heading tags, ensure that the page title, page name and first heading all have the same text, and so on.

The visitor pattern has a feel similar to a SAXParser parser for XML in that the framework calls my visitor with various events as it works its way through the model. One big difference is that the Quiotix DOM exists and persists so I can run several visitors against it one after another. They can each modify the DOM, and a final visitor, again thoughtfully provided by Quiotix, writes the DOM back out to HTML.

Dependencies

There is an unavoidable circular dependency loop between the DOM objects and the visitor. The DOM.accept method has a visitor parameter, and the Visitor.visit method has a DOM object parameter. Such dependencies between closely related classes are accepted in modeling. 

But what about dependencies between the Quiotix parser and my little application? A circular dependency there would be bad. And obviously there is no such circle, because the guys at Quiotix knew nothing about my application which came along years later. Circular dependency between packages was avoided by defining the visitor interface in the Quiotix package. My application depends on Quiotix, but not the other way around. It's all good.

Visiting Hours are Over?

A quick search on the web shows some controversy about the visitor's value. See these links for some people who at the very least have found situations where something else worked better for them.

I remain convinced that the Quiotix parser took an appropriate approach. The author made it easy for me to perform functions against all the objects in the DOM. If I were building my own DOM and an application that works on it, I might be tempted to build my own tree walker instead. But in this situation, where a vendor provides a closed model, the visitor is a handy pattern.