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.