Today, I want to talk about a problem that I don’t have a good solution for, and throw the floor open in the hope that someone else does. Teach me, O commenters.

Suppose your program has to build a tree structure — for example, as the result of parsing a query in some kind of structured query language. And suppose that, having built the tree, you want to do several different operations that involve walking that tree. How do you design that program?

To make it concrete, consider my (rather aged) package CQL-Java, which — get ready for a big surprise — provides a CQL parser in Java. CQL is a conceptually simple but very precise and expressive query language used in information retrieval, but for our current purposes all we need to know is that it supports structured boolean queries like these:

kernighan kernighan and ritchie (kernighan and ritchie) or fowler kernighan and (ritchie or pike)

Our task is to parse such queries, and to be able to render them out either in an XML format called XCQL or in Index Data’s ugly but functional Prefix Query Format.

I guess we can all agree that we want an API something like this:

abstract class CQLParser { Node compile(String textualQuery); } class Node { String toXCQL(); String toPQF(); }

And that the Node object returned from the parser is the root of a tree of nodes which represent the parse tree, where nodes can be of types And, Or and Term. (In reality there are lots of other kinds of node, too, the we can ignore them for our purposes.)

Assuming that we’ve written the actual parser, the question becomes, where do we put the code that generates the formatted textual output that becomes the XCQL and PQF? In the old days, especially if we were used to writing C, we’d have a pair of functions defined on the Node class, both switching on the node’s type and looking more or less like this:

Class Node { String toPQF() { if (type == AND) { return "@and " + left.toPQF() + " " + right.toPQF(); } else if (type == OR) { return "@or " + left.toPQF() + " " + right.toPQF(); } else { return term; } } }

These days, of course, no-one would be so crass. We’ve all read Fowler’s Refactoring, and we know that Switch Statements Are Evil. In particular, we’ve read Fowler’s page 223, the introduction to his Replace Type Code with Subclasses refactoring, and we remember that it says:

Switches or if-then-else constructs […] test the value of the type code and then execute different code depending on the value of the type code. Such conditionals need to be refactored with Replace Conditional with Polymorphism […] The advantage of Replace Type Code with Subclasses is that it moves knowledge of the variant behaviour from clients of the class to the class itself. If I add new variants, all I need to do is add a subclass. Without polymorphism I have to find all the conditionals and change those.

So we’d be more likely to make Node an abstract class, and derive three subclasses, AndNode, OrNode and TermNode. Then each of those subclasses would have its own little toPQF() method — and toXCQL() — and the Evil Switch Statement is avoided:

class AndNode extends Node { Node left, right; String toPQF() { return "@and " + left.toPQF() + " " + right.toPQF(); } } class OrNode extends Node { Node left, right; String toPQF() { return "@or " + left.toPQF() + " " + right.toPQF(); } } class TermNode extends Node { String term; String toPQF() { return term; } }

And all is well with the world.

Or is it?

It’s certainly true, as Fowler points out, that if you later need to add support for a new node-type — let’s say a unary NOT node — you can do that by making a single new subtype, NotNode, say, and tweaking the parser to generate it. Then the PQF generator is extended by the addition of NotNode::toPQF(), which returns “@not ” + subtree.toPQF(). It’s neat, clean and satisfying.

The problem comes when you want to add a new back-end — for example, when you want to be able to translate CQL into the query language of the Solr database. Now that our Node class is split into four subclasses (and in reality, many more), we have to mess with all of those subclasses, adding a toSolr() method to each of them. Whereas if we’d stayed with the old-fashioned single-node-class-with-a-type-indicator representation, we’d have to add only a single new method, Node::toSolr().

In fact, I am going to take the low road here, and rewrite the Fowler quote above:

Such class hierarchies need to be refactored with Replace Polymorphism with Conditional […] The advantage of Replace Subclasses with Type Code is that it centralises knowledge of the variant behaviour from being spread among many classes to concentrated in one. If I add new operations, all I need to do is add a method. With polymorphism I have to find all the subclasses and change those.

To be clear, I am not saying that the single-class-with-type-indicator approach is always and necessarily better. But I am a bit mystified that the subclasses-with-polymorphism is so universally and uncritically accepted as The Right Way, All The Time.

Even Fowler, who to his credit is usually careful not to insist on a One Right Way, seems to lose his moral compass when it comes to this particular design choice. Although I’ve been critical of the more unthinking Fowlerite followers, I actually really like lots of things about Fowler himself: for example, his catalog of refactorings includes several complementary pairs, showing that he doesn’t in general consider that there is One Right Way. (Pairs include: Extract Method vs. Inline Method; Hide Delegate vs. Remove Middle Man; Change Value to Reference vs. Change Reference to Value.) But against this backdrop, his insistence that conditionals should always be replaced by polymorphism (note the imperative “need to be” in the quote above) is all the more puzzling.

The situation is this: when you have to implement n functions (such as the toPQF(), toXCQL() and toSolr() methods above) across a class hierarchy of m classes (such as the AndNode, OrNode, TermNode and NotNode classes above), you need n×m code fragments — one to implement each function for each class. The old-fashioned approach uses n functions each consisting of a switch statement covering the m versions; the fashionable approach uses n×m small methods, split across the m classes — which in practice always seems to mean m different source files, at least if you’re writing Java. It seems to me that the choice of which is better depends on the relative sizes of n and m, and on how likely each is to change. If you’re going to need lots of new types, then, yes, a class per type is the way to go; but if the set of types is fixed (as in the CQL parser where the structure of the parse tree is determined by the formal specification of CQL itself), and if many new functions are likely to be needed, then the old way may be the best.

In other words, you can slice your two-dimensional code-fragment matrix either vertically into classes, or horizontally into functions. It seems obvious to me that different slicing directions are going to be better in different circumstances. A reflexive Replace Conditional with Polymorphism reaction is not going to help. (And when did the switch statement become synonymous with evil, anyway?)

And what about the distressingly common situation where you don’t have access to the original source-code, only object code? What would you do if I’d distributed CQL-Java only as a .jar and not as source, and your job was to use it to generate Solr queries? How would you implement the toSolr() method?

If you’re using Java, I don’t think you have any realistic option but to write a method like the first toPQF() that I listed near the start — a method that takes a Node as an argument, then interrogates it to figure of which of the Node subtypes it really is, and switches on the result, invoking itself recursively as needed. So in that common case you’re back to square one anyway.

In more dynamic languages such as Perl, Python and Ruby, you can use the technique known variously as monkey-patching or duck-punching: just go ahead and extend the classes. Add toSolr() methods after the event for AndNode, OrNode and the rest. I did exactly this in 2007, in a Perl module called Simple2ZOOM, not previously having heard of the technique, and consequently feeling a bit dirty — I used it to add a toCQL()method to a parse-tree representing a PQF query, the opposite of what we’ve been considering here. Now that the technique has a name, I feel a bit better about it. It’s actually a pretty cool way to get things done, although like most powerful techniques, it is prone to abuse if not done with taste and judgement. (See also: Lisp macros, continuations, C++ templates, and well, pretty much all Perl programming techniques.) I think the jury is out on whether monkey-patching is a good approach for this problem or not.

Is there a better way? I don’t really know. The Ruby way would be to pass a closureblock into a parse-tree walker provided by the library. That closure would get invoked for each node in the parse-tree, and would … well, that’s where it falls down: each invocation of the closure would have to ask the node what its type is in order to know what to do, so we’re really back at the first version again.

OK, I’m done. Will someone please tell me what obvious idea I am missing?

Update (a couple of hours later)

Quite a few people have suggested the Visitor pattern (from the Gang of Four book). The best exposition I’ve seen of this is in munificent’s detailed comment at Reddit, which I commend to you all. (Apart from anything else, he expounds both the pros and the cons of this approach).

I’m not wholly convinced that this is the cleanest and simplest approach, at least in dynamic languages; but it is, at least, nd excellent workaround for static languages. For more thoughts on Visitor, see my earlier comment below in response to Phillip Howell.

Also: it turns out (also as mentioned by several commenters) that the conundrum I described here is not only a well-known one, it even has a name: the Expression Problem.