The Visitor Design Pattern

Balancing Forces

The second of the reading for Session 14 focused on representing the abstract syntax tree in a program. We were left with an undesirable situation whichever way we went:

Can we achieve the best of both worlds? To do so, we need a way to add behavior for manipulating ASTs without having to modify the AST classes themselves. But we also need a way to expose type information dynamically without casting. Let's consider how we can implement abstract syntax trees in an object-oriented language in a way that balances these competing forces, using the Visitor pattern.

Using Visitors to Implement Abstract Syntax in an OO Language

The Visitor pattern is a design construct that balances the forces between working with objects and adding new behaviors. This pattern uses a more general pattern, the type-revealing message, that is commonly found in dynamically-typed languages. By revealing type information through messages, the programmer does not have to use typecasts, and the object does not have to make its instance variables public to all objects. +

In code...

To add a new behavior to the system, we need only create a new kind of visitor, with one method for each type of node. This is very similar to writing a new procedure with an arm for each type of node.

The primary strength of this approach is that it is relatively easy to add behaviors to the system, without having to edit the tree node classes themselves. Note, though, a big advantage of this approach. Visitor allows us to do type-driven programming with objects that does all type-checking at run-time.

As with any design construct, there are also costs. We must write a lot of little methods in the visitors. If we add a new type of thing to our system, then we must edit (and perhaps re-compile) all of the visitor classes! In essence, this technique trades OO benefits and costs for procedural benefits costs, in an OOP setting.

You can find a longer description of the Visitor pattern on Wikipedia.

Bonus Code: Implementing Abstract Syntax Using Visitors

What might it look like for our little expression grammar? Consider this implementation:

Notice the interplay between accept() messages that are sent to Expressions and visit() messages that are sent to AstVisitors. Perhaps now you understand why visitor is an example of what is called double dispatch.

Notice, too, that the composite objects AdditionExp and MultiplicationExp delegate to their operands, which are themselves Expressions. In a functional or procedural solution, the sub-expressions would be processed by a recursive call. What you are seeing is the object-oriented version of recursion — a composite delegating to simpler components, which ultimately reach the "base case" in the form of concrete components. This has been documented as a design pattern in its own right, called Object Recursion. (Be sure to check out the acknowledgements in that paper! :-)

You will find all of this code in the zip file for Session 14.