Patterns I Love/Hate #3: Visitor

19

Apparently my previous pattern posts on Singleton and Template Method hit a nerve based on comments and traffic – thanks for reading! I’m now moving the series from patterns that I hate to patterns that I simultaneous love and hate, because hey, I’m a complicated man (and no one understands me but my woman).

The Visitor pattern is one that I’ve spent lots and lots of time using and it has unquestionably been a key part of many systems I’ve designed and used. At the same time, there are aspects of the Visitor pattern that drive me nuts. I’ll try to explore both sides in this post. Note: I’m primarily a Java guy and this discussion of visitors is definitely Java-centric in a number of respects.

Dropping by for a visit

The Visitor pattern is typically used when you have a multi-object data structure (often something tree-like) and you want to traverse the data structure and implement various algorithms over it. It will help to have an example, so I’ll spare you the canonical Car and Shape examples. In honor of the upcoming Simpsons movie, let’s imagine a Simpsons game that has characters, props, etc. Here’s a bunch of data classes we might want to work with:

Visitor tends to be used in Composition hierarchies and is especially useful when these hierarchies are heterogeneous. When Composition hierarchies are homogeneous (a tree full of the same node class) the visitor pattern is still useful but you can get many of the same benefits without it.

In our game you see a lot of different kinds of Entity objects, including some that use composition to aggregate objects at various levels in the hierarchy and a Game object that is not even an Entity but holds all the Places. It is a key benefit of the Visitor pattern that all of the concrete data classes you want to visit do not have to share a common class hierarchy.

Now, let’s say we want to find all characters in the game that have dialog to process. We’ll need to walk through all the Places, then through all the Characters in each Place and collect all the dialog. One way is to just add the methods in all the classes involved:

public class Game {
// … code managing places
public void collectDialog(Map<Character,String> dialog) {
for(Place place : places) {
place.collectDialog(dialog);
}
}
}

public abstract class Place {
// … code managing Characters and Props
public void collectDialog(Map<Character,String> dialog) {
for(Character character : characters) {
dialog.put(character, character.getDialog());
}
}
}

public abstract class Character extends Entity {
public abstract String getDialog();
}

public class Homer extends Character {
public String getDialog() {
return “Donuts. Is there anything they can’t do?”;
}
}

// etc for each character

// To execute:
Map<Character,String> dialog = new HashMap<Character,String>();
game.collectDialog(dialog);

So, that works fine. But you might find that these sorts of methods tend to multiply. I might need to update the movement of all Characters and movable Props in the game in a time step. Or enter a Krazy Krusty mode where every Character turns into Krusty and some Props turn into squirting flowers. Or you might just need to find all of a particular kind of a Prop across the game.

The problem is that each time you find a new algorithm you want to implement over this data structure, you have to change some or all of the concrete types in the data structure.

Another way to look at the commonalities between these algorithms is that they (1) navigate a data structure and (2) perform some action at (potentially) all instances in the data structure.

Visitor is a pattern that pulls these two items out of the data classes and replaces it with visitor-agnostic code in each data class. The algorithm-specific code is put in a class (the visitor) dedicated to the algorithm instead. Let’s look at how the visitor pattern could be used in the example above. I’ll show just the visitor-related code here. First, the concrete data classes each need to have an acceptVisitor method:

public class Game {
public void acceptVisitor(GameVisitor visitor) {
visitor.visit(this);
for(Place place : places) {
place.acceptVisitor(visitor);
}
}
}

public abstract class Entity {
public abstract void acceptVisitor(GameVisitor visitor);
}

public class Refrigerator extends Prop {
public void acceptVisitor(GameVisitor visitor) {
visitor.visit(this);
for(Prop prop : getFood()) {
prop.acceptVisitor(visitor);
}
}
}

public class Homer extends Character {
public void acceptVisitor(GameVisitor visitor) {
visitor.visit(this);
}
}

// other Character and Prop subclasses are similar to Homer

public abstract class Place extends Entity {
public void acceptVisitor(GameVisitor visitor) {
for(Character character : getCharacters()) {
character.acceptVisitor(visitor);
}
for(Prop prop : getProps()) {
prop.acceptVisitor(visitor);
}
}
}

public class Moes extends Place {
public void acceptVisitor(GameVisitor visitor) {
visitor.visit(this);
super.acceptVisitor(visitor);
}
}

// other Place subclasses look the same as Moes

Each of these acceptVisitor methods has two responsibilities. First, it must call back to the visitor. This is the key to the whole pattern as this callback leverages the static type of the this object to correctly call the right method on the visitor without needing some sort of horrible if/else or switch block. (You may know this callback by the term “double dispatch”.) Second, the acceptVisitor method must traverse “down” the data structure to any contained elements (we’ll come back to this later).

Then we need to look at the actual visitor code itself.

public interface GameVisitor {
public void visit(Game game);

public void visit(Apu apu);
public void visit(Bart bart);
public void visit(Homer homer);

public void visit(Moes moes);
public void visit(NuclearPlant plant);
public void visit(SimpsonsHouse house);

public void visit(Donut donut);
public void visit(DuffBeer beer);
public void visit(Refrigerator fridge);
}

public class CollectDialog implements GameVisitor {
private Map<Character,String> dialog = new HashMap<Character,String>();

public Map<Character,String> getDialog() {
return dialog;
}

public void visit(Game game) { }
public void visit(Apu apu) {
dialog.put(apu, apu.getDialog());
}
public void visit(Bart bart) {
dialog.put(bart, bart.getDialog());
}
public void visit(Homer homer) {
dialog.put(homer, homer.getDialog());
}

public void visit(Moes moes) { }
public void visit(NuclearPlant plant) { }
public void visit(SimpsonsHouse house) { }
public void visit(Donut donut) { }
public void visit(DuffBeer beer) { }
public void visit(Refrigerator fridge) { }
}

// To execute:
CollectDialog collectDialog = new CollectDialog();
game.acceptVisitor(collectDialog);
Map<Character,String> dialog = collectDialog.getDialog();

Here the GameVisitor defines the basic Visitor interface that each algorithm must implement. The interface consists simply of a visit method for each concrete type. In the CollectDialog visitor, we simply ask the characters for their dialog on those visit methods and do nothing on all the other visit methods.

So, there is clearly more code here than in the original example. The difference is that we are now set up to add new algorithms at will without modifying any of the data classes. So, adding Krazy Krusty mode is as simple as adding one visitor class, instead of modifying many or all of the data classes. Another nice benefit is that you don’t have to start from the Game class; you can start from any class in the data structure and traverse down through the structure.

Pretty cool stuff. Visitor lets you “bake in” the ability to support any number of algorithms over a heterogeneous data structure without modifying the data structure as algorithms are added.

So what’s to hate?

As you might expect, some of the very things that make Visitor useful also make it frustrating. Here’s a list of issues we’ll explore:

  • Expanding data structure breaks visitors
  • Navigation strategies
  • Return values
  • Exception handling
  • Execution and use of visitors
  • Performance

Expanding the data structure

The first aspect of the Visitor pattern to note is that the GameVisitor interface contains a visit method for every concrete class in the data structure. So, with the flexibility we’ve gained in adding new algorithms, we’ve given up the flexibility to expand the data structure without breaking our existing visitors. In the current implementation, The addition of a new character such as Lisa would break the visitor framework because the Lisa’s acceptVisitor method needs to call back to a visit(Lisa) method on GameVisitor that does not exist.

If you completely control all visitors all your data structure, this may not be an issue. It is simple to add a visit method to GameVisitor and update all existing visitors appropriately.

However, if you want a bit more insulation and protection from this kind of change, I would recommend introducing an abstract base class for your visitors that implements all visit methods with a no-op implementation. Once you have this structure in place, the addition of a new class requires:

  • Adding a new data structure class
  • Adding a new visit method to the GameVisitor interface
  • Adding a new no-op visit method to the BaseVisitor interface

No other visitors are affected from a compilation point of view. They still may need to be reviewed for whether they should override the default dummy visit method in BaseVisitor.

Another nice side effect of this change is that we can remove the empty methods in our CollectDialog (or any other visitor) as they are already implemented as a no op in the base class.

Navigation strategies

One gross part of the visitor pattern (as implemented above) is that each class with “children” must traverse its children in the visit method. This means that navigation logic is distributed throughout the data structure and is likely to get overlooked as the class evolves. Perhaps more importantly, it means that there is exactly one fixed navigation strategy hard-coded into the structure of the class. For simple data structures with obvious traversal strategies, I think this is fine. But it’s certainly not the only choice.

One common option for moving this logic somewhere is to build an Iterator or Strategy that knows how to traverse the composition hierarchy. Each data class then calls the Strategy then knows which object is the next to traverse.

You can actually even use double dispatch again to implement an Iterator as a co-operative class that works with Visitor. This allows you to separate the navigation class hierarchy from the visitor class hierarchy. Two big downsides to this approach is that the call sequence is much more complex and that the visit() and iterate() methods of every class visited so far are left on call stack. This once culminated for me in blowing the thread stack limit on some particularly large (but shallow) data structures. I don’t really recommend this option.

Another interesting way to think about the navigation logic is that it is itself like a visitor – it is a bit of data class specific logic that must be executed to determine what children each data class contains. Using this intuition, you can implement your navigation logic itself as a visitor that does nothing but say how to navigate when asked to visit. You can then give the navigation visitor a more traditional “logic” visitor that rides along on top of the navigator.

Let’s look at an example of this:

// Will be needed inside the navigation visitor to have a common generic type
public interface Visitable {
void acceptVisitor(GameVisitor visitor);
}

public class NavigationVisitor extends BaseVisitor {
private GameVisitor logicVisitor;
private LinkedList<? super Visitable> itemQueue =
new LinkedList<Visitable>();

public NavigationVisitor(GameVisitor logicVisitor) {
this.logicVisitor = logicVisitor;
}

private void visitNext() {
if(! itemQueue.isEmpty()) {
Visitable first = (Visitable) itemQueue.removeFirst();
first.acceptVisitor(this);
}
}

public void visit(Apu apu) {
logicVisitor.visit(apu);
visitNext();
}

public void visit(SimpsonsHouse house) {
logicVisitor.visit(house);
itemQueue.addAll(house.getCharacters());
itemQueue.addAll(house.getProps());
visitNext();
}
}

Here we see a NavigationVisitor that holds a queue of items to be processed by the visitor. Each visit method visits the logic visitor that is riding this navigation visitor. It then adds any children for the current data item to the queue (if any). And finally, all visit methods must call the visitNext() method to pick off the next item in the queue and visit it next. This implements a breadth-first traversal of the data structure. It’s also trivial to implement depth-first and you can also change whether you visit the nodes in pre-, in-, or post-order.

You might also have some custom traversals that always need to walk Characters first or Props first or whatever. Doing that is as easy as adding a navigation visitor.

One classic bug that occurs frequently when using a navigation visitor is the case of calling acceptVisitor with the logic visitor without using a navigation visitor at all. Since the navigation logic no longer exists in the concrete classes, if you don’t use a navigation visitor then you will not traverse any objects except the first one.

Return values

Return values are an area where visitors fall down a bit. The traditional way to return or collect values while visiting is to store the state in the visitor, then provide getters to retrieve the state from the visitor at the end. This works and is the usual way to do this but also feels repetitive and clunky after you write a few visitors.

Another cleaner way to do this was suggested recently by Ricky Clarkson by using generic visit methods that bind the return type of the visit method to the type of a visitor. I have not tried this in my code yet but it seems like a very interesting idea. The one place where it doesn’t look so great is when the visit method does not need to return a value. In that case, the visit method must right now return null. Ricky suggests that allowing us to specify <void> would be a nice way to solve this problem in Java 7.

Exception handling

Exception handling always sucks with visitors. You can’t put a visitor-specific exception on the visit() methods as this would also affect the base interface and all acceptVisitor() methods. So you are left with several options:

  1. Store as state in the visitor class
  2. Declare throws Exception (or other more constrained type) on all methods
  3. Throw exceptions from your visitor, but use only RuntimeExceptions

I’ve tried all of these at one point or another and can’t say I like any of them. The first option is the weirdest in some ways since it subverts the expected use of exceptions, but probably the one I’ve liked the best. It allows you to store exactly the right kind of exception in your visitor and still not need to declare it on the visit() methods.

The second option forces you into catching Exception on callers of all the visitors. There are ways to trap this and rethrow the real exception (see next item) but this option just feels wrong.

The third option is workable but it probably depends upon how friendly you are with the idea of doing away with checked exceptions and moving instead to using mostly unchecked exceptions. If you’ve already done that and are mostly using unchecked exceptions in your code, then this will be easy for you. If not, this will most likely seem weird and scary as you will start catching specific runtime exceptions when you call visitors (none of which are declared in the signatures).

Execution and use of visitors

Another item of visitor weirdness is the way in which they are invoked, particularly when doing navigation via a visitor:

CollectDialog collectDialog = new CollectDialog();
NavigationVisitor nav = new NavigationVisitor(collectDialog);
game.acceptVisitor(nav);
Map<Character,String> dialog = collectDialog.getDialog();

These four lines will be repeated in every visitor usage scenario. Generally it useful to build a couple helper methods to make this cleaner. First, it’s useful to wrap up the middle two lines in a static method in NavigationVisitor:

public static <T extends GameVisitor>
T execVisitor(T visitor, Visitable visitable) {
NavigationVisitor nav = new NavigationVisitor(visitor);
visitable.acceptVisitor(nav);
return visitor;
}

You can then execute a visitor with navigation with just:

CollectDialog collectDialog =
NavigationVisitor.executeVisitor(new CollectDialog(), game);
Map<Character,String> dialog = collectDialog.getDialog();

And it can be helpful to push this code into a static method in CollectDialog itself:

public static Map<Character,String> collect(Game game) {
CollectDialog collectDialog =
NavigationVisitor.executeVisitor(new CollectDialog(), game);
return collectDialog.getDialog();
}

One other nice thing you can do if you are storing exceptions within your visitor is to check for and actually throw them from the static helper method. This makes the exception handling more normal for callers of the static method.

Another item I want to mention is that one side effect of using the Visitor pattern is that you will have lots of public visit() methods in each visitor class and these methods should NOT be called by anyone outside the visitor. The methods typically must be public to be called by data classes in a different package. There’s not really any workaround for this issue – it is something that should be documented in the javadoc though (which of course, no one will read).

Performance

I’ve already mentioned some memory considerations when using an Iterator in conjunction with Visitor. You may have noticed in the visitor examples above that we are now traversing the entire data structure looking for dialog when the original implementation only looked at Characters in Places. In my experience, this is a common occurrence with visitors. It’s so easy to implement a new visitor that we don’t worry about how much extra and unnecessary work is occurring. This is also something that’s a little difficult to spot with a profiler because the extra overhead is spread over a bunch of methods that traverse the data structure unnecessarily so no one method shows up as a hot spot.

There are a handful of fairly easy things you can do to address visitor performance as you are building out your solution:

  1. Add a reset() method to allow reuse of visitor instances. The visitor instance can typically be reused, thus saving the cost of constructing the visitor instance. They’re almost certainly not worth pooling (like most Java objects) but when used multiple times in the same thread, there is no reason not to reuse them. I’ve found that having a standard reset() method on the visitor interface forces implementors to implement this behavior and makes it a common part of the visitor usage in your system. If you are wrapping your visitors with static helper methods, those methods may be able to store and reuse visitor instances, perhaps in a ThreadLocal.
  2. Add a mechanism to abort/terminate a visitor. It is useful to have some way to abort the visitor execution for visitors where you are finding something and do not need to visit the entire data structure. Depending on where navigation logic lives, there are different ways to do this. In some cases you’ll want some way to store an abort flag on the visitor itself and in some cases you’ll want to build this into your navigation logic. You will definitely want an abort mechanism if you are storing exceptions in the visitor state, as you don’t want to continue traversing the data structure once an error has occurred.
  3. Create custom navigators that only visit part of the data structure. In the case of our CollectDialog visitor above, we know that we only want to visit a portion of the data structure, so we could write a navigation visitor that only visits Places and Characters and skips Props entirely. In general, I recommend doing this sparingly for navigation cases that are frequently used or for cases that show up as performance hot spots. The reason is that if you create a custom navigator and visitor for every algorithm, you are writing a lot of extra code and not getting the reuse benefit of the navigation logic. One alternative might be to just have a single, very configurable navigator. I haven’t done that before, but I imagine it would work.
  4. Create a custom navigator with “stop” nodes. One variant of the prior idea that I’ve found useful is the notion of “stop” nodes. For example, in the case of an AST (abstract syntax tree) generated by a parser, you may want to traverse some portion of the syntax tree but “stop” once you hit some kind of node. For instance, you might traverse an AST representing a SQL query SELECT clause and “stop” at nodes representing aggregate functions. Maybe this technique is a bit specialized but I found that it was perfect for some classes of visitor.

What are the alternatives

One obvious alternative is to simply hardcode the navigation and collection logic, either in the concrete objects or completely outside them in helper classes. For simpler data structures, this makes good sense. As we’ve seen above, there are all sorts of consequences of using the Visitor pattern.

Another interesting future possibility in Java is closures. The notion of passing execution logic into some sort of navigator could also be solved quite naturally using closures. Some of the same issues with return values and exception handling still exist and are the cause for a lot of the complexity in the BGGA closure proposal for example.

Examples

I’ve built full examples of some of the earlier code samples and you can grab them in this convenient zip to play with them yourself. Each package contains a complete and independent version of the same code:

  • v1 – original non-visitor example
  • v2 – rudimentary visitor example
  • v3 – use of a visitor base class
  • v4 – use of navigation visitors

Thanks again to everyone that has read, commented, or emailed on prior posts. I’ve enjoyed the conversations and learned a lot.

Comments

19 Responses to “Patterns I Love/Hate #3: Visitor”
  1. daniel says:

    Excellent article. But I wonder about your advice #1: reusing visitors. Are you sure there is anything to gain there? I’d refer to Brian Goetz article on “Writing Better Code” – http://java.sun.com/developer/technicalArticles/Interviews/goetz_qa.html
    and the risk of “Wrong Intuitions About Performance Problems”.
    Cheers, and thanks a lot for this visitor article!

  2. Alex says:

    @daniel: Yeah, this is a minor thing. Certainly not worth an object pool but if you’re going to construct and use the same visitor instance twice in a row, you might as well make one instead of two.

  3. Neal Gafter says:

    You can make the thrown exceptions type parameters of the visitor type. But unfortunately you can only do a fixed number of them. The exception transparency support in BGGA will also allow you to have a single type parameter in a visitor type represent multiple (or no) thrown exception types.

  4. Alex says:

    Thanks Neal – I’ve heard that idea but never tried it myself. I think for the cases I’ve seen, having a single exception type would be sufficient.

  5. Taylor says:

    I agree with everything you said – and it’s a nice review of the pattern.

    I think though, that in practice, the comment regarding the need to change the Visitor every time more data structures added is probably not entirely accurate in real life, since that would likely be done for a group (parent class) of objects that would all have the same behavior, therefore adding a method for a single super class would implement the behavior for all of the subclasses, which is the most likely kind of addition in the real world.

    Agreed that when the model undergoes big changes or additions, one has to go and update the Visitor, but somehow you have to update the code, right? I think your complaint is more around the separation of responsibilities that is not immediately obvious – though I think because of the double-dispatch nature of your code, the compiler will flag a missing Vistor method.

  6. Alex says:

    @Taylor: I think you misunderstood my point . If you add a new concrete data class and you want your visitors to visit that class, a new visit() method must be added to the Visitor interface, which will then break all existing visitor implementations…unless you have a base class for your visitors that protects you from that.

  7. Karsten says:

    I think you used the visitor-pattern in a wong way which lead to some of your problems. The code for iterating over the child nodes doesn’t belong in the accept-method. A accept-method for the visitor-pattern must always have the form

    void accept(Visitor visitor) { visitor.visit(this); }

    Everything else makes the pattern less useful. The iteration of child nodes always belongs to the visitor and not to the visitee.

    The big advantage of visitors is that it’s easy to write a generic visitor and create specialized versions of it by using inheritance. So by using this, you only have to write your iteration code once and inherit or override it, where necessary. So you have both flexibility and code reuse.

    About exceptions: I always use non-checked exceptions with visitors. It’s the same problem like using closures. It may be fixed in future Java, but in the moment it’s simply a byproduct of checked exceptions. But since Java also allows for unchecked exceptions it’s only a minor annoyance. It’s like using casts and instanceof: In most cases it’s wrong, but sometimes theres no way around it.

  8. Alex says:

    @Karsten: The code for iterating over child nodes can be put in a number of places; there is no one magical right place to put it. It’s been a while since I’ve read it, but I’m pretty sure GoF actually has a discussion of exactly this issue and discusses the possible places to put the logic.

    I was trying to start with the simplest possible option and I think embedding the navigation logic into the visit() methods is the simplest place to start. But I see much value in pulling the navigation logic out of the data class entirely and reducing all visit methods to just the visit call.

  9. Karsten says:

    Yes, it’s discussed in the GoF book. But from my experience it’s a big mistake to do iteration in the accept-method because it limits the flexibility of the visitor. What if you want to skip iteration of Food nodes in the Refrigerator-class for certain visitors? By putting all the logic into the visitor, you are free to do what you want or need ‘by-visitor’. With this your ‘stop’-problem ceases to exists because the visitor can abort computation whenever it wants.

    BTW: Don’t reuse visitors. It’s tempting and may work sometimes, but in general it’s not worth the potential problems. Because visitors often have state, reusing them can create difficult to locate problems with multithreading and recursion. Better allocate a new one where needed. The overhead should be neglectible most of the time, because the program spends much more time doing the visiting compared to the allocation.

    I think in an article about fundamental issues like this, it’s important to be aware of things like this. Visitors are still not often used correctly and I think it’s one of the most misunderstood GoF-pattern.

  10. Great article. I personally don’t like the visitor pattern to the extent that in my pet project, Pattern Enforcing Compiler (PEC), I wrote multiple dispatch instead. PEC is a Java compiler that extends the type checking to understand patterns, its multiple dispatch pattern is described:

    http://pec.dev.java.net/nonav/compile/javadoc/pec/compile/multipledispatch/package-summary.html

    The advantage of multiple dispatch is that it solves the issues of the visitor code being obscure in that with visitor you do a call back then the traversal. With multiple dispatch you just traverse the data. Also with multiple dispatch if you add a new class you don’t have to change anything, e.g. if you added a new character you would just add the methods for that character and you would not have to modify any other classes.

  11. Alex says:

    @Karsten: I’m in complete agreement with you on navigation logic in the accept method. But I don’t think it’s wrong, just not the best option. In cases where the navigation needs are very obvious and you don’t need the flexibility, it is simpler than doing anything else. And in my book, simplest possible wins, especially at the expense of flexibility you know you don’t need. I’ve had the most success with using navigation visitors, which is why I fleshed that out later on and recommended it.

    On visitor reuse I’ve had success with reusing visitors in thread-confined scenarios where I’m doing LOTS of visiting on the same visitor. I agree it’s not something for every scenario, but it’s a tool for the toolbox.

    @Howard: Very cool – I’ll check it out. You might also be interested in the comments for this article on reddit.

  12. - says:

    Good article, thanks!

  13. Jonathan Allen says:

    A college and I were reviewing the GoF book last night and he came across something interesting:

    The whole purpose of the Visitor pattern, according to them, is to avoid adding new methods to the existing data classes. That’s it. It wasn’t actually meant to solve the problem of ‘visiting’ every object in the collection, it was just to add functionality to a class without it actually being in the class.

    Or in other words, it is a way to implement a non-OO style in an OO langauge.

  14. Stan says:

    A couple times I have been delighted as a client of a complex DOM to find Visitor implemented by the DOM author. Now I don’t give a hoot about the navigation method, except to smile because I don’t have to write it. I’m just a happy consumer of the DOM.

    Also Visitor on a tree somehow pleases me in the same way the SAX parser does. A SAX handler and a Visitor can feel an awful lot alike. In both cases I take on some responsibility for knowing state – where I am in the tree – which may be an issue at times.

  15. prashant says:

    Alex,

    Thanks Alex.I love your blog and especially your Java7 updates.

    Thanks
    Prashant
    http://prashantjalasutram.blogspot.com/

  16. soylent says:

    Nice transcript of “Design Patterns: Elements of Reusable Object-Oriented Software” (ISBN 0-201-63361-2). Could only recommand the book to everyone as it discusses patterns indepth which also includes sections which discuss the draw-backs and “not-to-use” scenarios.

    Regarding Performance optimisation:
    The First and Second Rules of Program Optimisation

    1. Don’t do it.
    2. (For experts only!): Don’t do it yet.

Trackbacks

Check out what others are saying about this post...
  1. […] Pure Danger Tech » Blog Archive » Patterns I Love/Hate #3: Visitor (tags: toread) […]

  2. […] Here’s one version of Visitor with closures. I’m not too happy with it yet. There’s no Visitor interface, I’m just using a closure to handle the visitor callbacks. The downside to that is that I’ve lost the double-dispatch part. I suspect this is probably fine for many common visitor cases and sucks for others. […]

  3. […] 3. Great overview and alternative implementation of traditional visitor pattern. http://tech.puredanger.com/2007/07/16/visitor/ Posted 21 May 2008 / C_CPlusPlus / […]