Main menu:

Site search

Categories

Archive

The Visitor Pattern: A false choice

I’ve been wanting to make a point about software development environments, tools, and languages by looking closely at the visitor pattern.

First let’s look at the usual OO way of defining a “Print” method for 2 child expression classes:

abstract class Expr {
abstract void Print(PrintStream out);
}

class IntExpr extends Expr {
int v;
IntExpr(int v) { this.v = v; }
void Print(PrintStream out) { out.print(v); }
}

class AddExpr extends Expr {
Expr e1, e2;
AddExpr(Expr e1, Expr e2) { this.e1 = e1; this.e2 = e2; }
void Print(PrintStream out) { e1.Print(out); out.print(” + “); e2.Print(out); }
}

Here’s the way of doing the same thing with the “Visitor” pattern:

abstract class ExprVisitor {
abstract void visit(IntExpr e);
abstract void visit(AddExpr e);
}

abstract class Expr {
abstract void accept(ExprVisitor v);
}

class IntExpr extends Expr {
int value;
IntExpr(int value) { this.value = value; }
void accept(ExprVisitor v) { v.visit(this); }
}

class AddExpr extends Expr {
Expr e1, e2;
AddExpr(Expr e1, Expr e2) { this.e1 = e1; this.e2 = e2; }
void accept(ExprVisitor v) { v.visit(this); }
}

class Printer extends ExprVisitor {
PrintStream out;
Printer(PrintStream out) { this.out = out; }
void visit(IntExpr e) { out.print(e.value); }
void visit(AddExpr e) { e.e1.accept(this); out.print(” + “); e.e2.accept(this); }
}

I picked three results from the first page of google results for “visitor pattern” to include in this conversation. The first is a page from the Nice project called Visitor Pattern considered useless.

The next is a good description of various alternatives including a method of using reflection to avoid some cut and paste: Using Reflection for the Visitor Pattern

Finally, there’s an interesting discussion of how the visitor pattern can help avoid performance problems with hibernate.

My simple point is this: The choice of whether or not to use a “Visitor” doesn’t seem like a design choice to me. It feels like a user-interface choice. The case of hibernate is an interesting one… but note to what extent it hinges on some pretty deep knowledge of the behavior of java and the underlying behavior of hibernate. This is definitely not the kind of stuff that’s going to show up in a more business-oriented model.

In the case of the “Nice” project, note how the proposed alternative is to use a new language. Is this really much better? The resulting solution admittedly looks nicer. But in my opinion, it’s hard enough to get people to learn the semantics of basic inheritance correctly. Adding more complexity to the language and forcing all readers of that piece of code to understand the new dispatch behavior is asking a lot.

To a lesser extent, I might say the same thing about the proposed implementation of the visitor pattern that uses reflection. I learned java in the early days before reflection was a widely-employed technique, and I admit that I don’t usually think of that as an option.

All of my opinions on these particular solutions are subjective, of course.

But the larger point is that choosing to use a visitor pattern (or some variant) seems to be a largely unnecessary instance of the “tyranny of the dominant decomposition”. Especially when that decomposition requires new language semantics.

My solution is to pull some of this complexity out of the language and out of the design and into the user interface of the IDE. I’m working on formalizing this, but the basic idea is to consider the difference between the usual OO decomposition and the visitor solution to be superficial. In the presentation layer.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google

Write a comment