Wednesday, August 16, 2006

Story of a Decoupled Visitor

I find visitor pattern mesmerizing. Master Uncle Bob has published great articles on visitor family of patterns (Acyclic Visitor, Visitor Family of Design Patterns) apart from others.

In his articles he illustrates how visitor can be applied to prevent interface bloating by introducing visitation support in the domain model (The Modem Hierarchy in the article) and how easy is to introduce new visitation based behavior without modifying the hierarchy.

So far so good, this approach works for the developments where you have the luxury of initial design. You may add support for visitation based traversal in the domain model or the layer extending it, or your hierarchy code generator, say antlr or JavaCC, may generate cool visitor based code so that its easy for you to compute metrics or do other stuff on the model (any sophisticated AST is a Model, I'll prefer calling class hierarchies a Model here).

Imagine we have a model wherein visitor is disliked alien (ayee! ugly! visitor is an implementation detail, not needed in my platform independent model!!!), in short, I've a "pure" domain model. But our model composition is so complex that normal iteration isn't going to give us big advantages, and we need extensibility as well.

I have faced similar design problem back when I was developing a intra-model transformation framework, where, the model was predefined without visitor supported api. During transformation, every now and then an element search from model was becoming inevitable, every searcher wanted to do something interesting and unique. For example, Let's say I want to find an element from source model and do a shallow copy of it and add it to target model and so on. I tried to fit visitor for this design problem, I wanted the extensibility without bloating the existing model.

I can't add the visitation API in the given model, but I can always create a utility (or add api to context object) which accepts model and visitor and does a generic traversal (as in loop based or recursive) as shown in the diagram figure.

Context holds the actual model and defines utility api to invoke visitor implementation. Leaving other details, each client (*ModemDiagnostic in this example) can create an implementation of Visitor and visit the hierarchy. Context might end up doing a list based iteration for full scan of the model which can be controlled from the visitor. This variant double dispatches for each model item but is totally decoupled from the domain model (there u go, greetings!!).

I've used this pattern repeatedly (BluePrint Foundry Transformation Framework, FileSytem Utilities) but I can't tell it a "regular visitor" or acyclic visitor, but I'll call it a Decoupled Visitor. Its easy to identify that design solution offered by this approach can be replace by iteration and template methods and inheritance, but then there's trade-off between inheritance and composition ans so is the story.

6 comments:

Ricky Clarkson said...

I think you're talking about an idea that I have had, but I can't tell. Can you show a bare minimum example of a class that isn't visitable, then how you'd make it appear visitable as described in this post?

I.e., code.

I'm still trying to work out whether my idea makes sense, I haven't even tried it out yet.

Nirav Thaker said...

ricky,

Take for example a basic composite

Pure model.
class java.io.File{..}

Now add visitation support,

public interface FileSystemVisitor {
public boolean visit(File file);
}

public static void visitFileTree(String rootPath, FileSystemVisitor visitor) {
File file = new File(rootPath);
if (file.exists() && file.isDirectory()) {
String[] members = file.list();
for (String member : members) {
File memberFile = new File(file + File.separator + member);
if (!visitor.visit(memberFile))
break;
visitFileTree(memberFile.getAbsolutePath(), visitor);
}
}
}

Then you can write different types of visitors...

This example is fairly easy, I've used this pattern in very very complicated UML2 Superstructure specification where I'd to do tens of different types of visitations and the domain model was freezed. I hope it makes sense.

Ricky Clarkson said...

Oh, I see. I'd rather use an iterator there.

I use visitor to give me multiple dynamic dispatch in a language that tries not to support it.

Nirav Thaker said...

Can't disagree that!

Well, IMO, in that case you have to write same ugly iteration code everywhere and visitor is always elegant to write, heavily reusable.

Ricky Clarkson said...

I've not yet found the foreach loop from Java 5 and above ugly.

Nirav Thaker said...

@Ricky

Good for you!

BTW, It's not just 'foreach' loop, that I'm talking about.