XpdWiki

FrontPage
RecentChanges
FindPage
PageIndex
UnusedPages
UndefinedPages
XpApprentices

Set your name in
UserPreferences

Edit this page



Referenced by
GettersAreEvil




JSPWiki v2.0.52


Visitor


The whipping-boy of patterns

This thing really seems to irk some people. They accuse it of being "complex", or "fancy", they don't want to mess around with all this stuff, they just want to "get the job done".

Of course, like any technique, overused an abused it obscures and confuses, but...

Blind poet coding

Here' s common Java idiom:

    Iterator i = composite.getIterator();
    while(i.hasNext()){
        StuffDoer d = (StuffDoer)i.next();
        d.doStuff();
    }

At a conference session once a delegate asserted that code like this was, yes, dull, repetetive, clumsy and error prone, but that this was mitigated by it being "Homeric", suggesting that for loops were the "wine-dark seas" of C programming, or similar. I think that's quite a dangerous metaphor: the repetetive idiom in Homer (and much other poetry) is intended to 1) allow the poet's voice to go onto autopilot while he remembers (or invents) the next bit, and 2) put the audience into a light trance, to enhance the imagery. Neither of these seems quite appropriate in the programming context. Worse yet, that delegate suggested that this sort of code becomes invisible to the programmer after a while. It hardly seems wise to expect programmers to train themselves not to pay close attention to their working medium!

When I look at code like that I get the same feeling as I do when, as is neccesary from time to time, I have the engine of my motorcycle running with the covers off: an otherwise benign system is rendered scary and dangerous by having its internal mechanisms exposed. I have to pay very, very close attention to what I'm doing, or else risk severe damage to at least one of myself and the machine. What is the mechanism here? Walking a composite.

Now, this style of code seems to be quite attractive to some developers. They seem to feel that it has a no-nonsense, getting-the-job-done sort of look to it. I feel that it looks unfinished and clumsy. Simplisitc, rather than simple.

Partly, this is because I have that feeling in my head like being close to an unshrouded duplex chain driven at 5000rpm ready to tear my fingers off, and/or splash hot ATF in my face. I somehow visualise the spikey little program counter whizzing around that loop, flinging off little blobs of logic in the conditional. Frankly, it scares me. I suspect that this is part of the attraction for some developers: this code looks busy, as if it's really doing something. Hence the impression of progress.

Busy, exposed mechanisms like this as a characterisitc of 19th and early 20th century mechanical design, that is, before anyone really knew how to do it right. Then, post WWI came RaymonLoewy . Loewy was often accused of taking a perfectly good product designed by someone else and merely putting a pretty face on it. But consider his first really successful design, the Gestetner spirit duplicator. These machines started out as clanking, harazdous horrors, but Loewy's redesign was so successful that it stayed in production for almost half a century. The mechanism was almost unchanged, but the shell (spuriously streamlined purely for the look of it as anyone, Loewy included, would admit) made the machine cleaner and quieter in operation, thus improving the working conditions of the operator, prevented paper dust and fumes from the machine spreading quite so far around it, and protected the mechanism from foreign bodies, thus improving the reliability of the device. And it looked sufficiently nice that the machine no longer needed to be hidden away in awkward corners of the office.

Visitor

So, how might we apply a bit of industrial deisgn to the code up above?

Well, whatever I'm writing this code for, pennies to pounds iterating over composites is not part of the problem domain that the customer is paying me to work in. So, exposing this mechanism is jarring to me, it is (contra to the Homeric argument) a distraction, a prompt to fall out of flow. Walking composites is a little bit of incidental complexity that I'd really rather have to deal with at most once.

Note that the client code is coupled to four methods on three interfaces, and it has some logic in it. Not much, but some.

Well, if one were to start refactoring the above, and were really keen on OnceAndOnlyOnce?, and on separating concerns, and testability, and Demeter and all that good stuff, one route (not the only one) leads to something like this:

    composite.accept(
        new StuffDoerVisitor implements Visitor{
            public void visit(Object o){
                ((StuffDoer)o).doStuff();
            }
        });

We're down to three methods on three interfaces and no logic. A small improvement, maybe, but how much more restful the code is. And supposing that we ever do this twice or more, cache the visitor, and bang, all of a sudden it's a one-liner

    composite.accept(doStuffVisitor);

The real joy of this is that all that incidental complexity is gone, and I simply express what I want done, and not how to do it. Of course, this code now appears not to do anything much, which isn't terribly exiting or macho, and doesn't look like nearly as much progress as the version at the top of the page.

Visitable

The line composite.accept(doStuffVisitor); relies upon composite knowing how to drive a Visitor. This is easily achieved.

class Composite implements Visitable ''etc'' {
    public void accept(Visitor v){
        Iterator i = iterator();
        while(i.hasNext()){
           v.visit(i);
        }
    }
    ...
}

And so now our loop comes back, but in a much less scary setting, he's back behind a cover.

An additional wrinkle with this guy is that he can be used to implement safe accessors without the overhead of copying:

        public Visitable thingsToOperateOn(){
                return new Visitable(){
                        public void accept(Visitor visitor){
                                things.accept(visitor);
                        }
                };
        }

see blocks and JUnit using blocks. Yes blocks is a bad name. -- OliBye

Faking it

It's a common criticism of many GoF patterns that they are "just" work-arounds for the limitations of C++ (and now Java) and that they disappear in more powerful languages. The problem with this assertion is the word "just".

All programming is working around the limitations of the language. Unless you are in the happy circumstance of being able to work in a language that already has all the primitives and means of combination that exactly map the concepts in your problem domain, a circumstance not found in the wild.

GoF

GoF has probalby done more to introduce patterns to the broader programming community than any other single artifact, and that's great. And yet there is a problem, too many people come to the conclusion that the examples in GoF are normative exemplars, rather than illustrative examples.

As such, a lot of folks might complain that the above code is "not Visitor" because it's rather different from the hairy AST-walking example in GoF. And indeed it isn't like that example. But...

The intent of GoF Visitor says "Represent an operation to be performed on the elements of an object structure." And continues, "Visitor lets you define a new operation without changing the classes of the elements on which it operates."

There's a subtle and interesting change of emphasis between those two sentences. The first states what a Visitor does, the second describes something that using Visitor affords. Neither of these talks about simulating multiple dispatch, which is what folks seem to focus on.

In fact, the discussion of Visitor in GoF is pretty unhelpful. There's actually something like two patterns in there, simple Visitor/Visitable, and a "subclassing visitor" that (as with many GoF patterns) makes heavy use of inheritance to obtain very rich beahviour. The description of the two is all munged together.

Its notable that the idea of a generic Visitable is not mentioned in GoF, altough the question of which object has resposibility for traversing the composite is mentioned in Implementation section. Three possibilities are mentioned: the composite, the visitor or an iterator. Wrapping an iterator up in a separate utility class isn't mentioned, which is a shame since doing that is what makes the technique really fly.

At least the pathological choice of the code at the top of the page, giving responsibility for traversing the composite to the client code that wants something done is not disucssed.

Simple Visitor/Visitable corresponds to the "Represent an operation to be performed on the elements of an object structure." bit of the intent, the "When the classes defining the object structure rarely change, but you often want to define new operations over the structure" bit of the Applicability, and the "Visitor makes it easy to add operations that depend on the components of complex objects". It's a small, powerful pattern in its own right, and leads naturally into the same terretory as PassSomethingIn, so it's a shame that its buried in amongst the bigger mutliple-dispatch stuff (valuable as that is when you really need it). --KeithB



see blocks

For things like

class Adder implements Visitor {
        private int total = 0;
        public Object evaluate(Object input) {
                total += ((Integer)input).intValue();
                return new Integer(total);
        }
}

With.all(new From(1).to(3)).evaluate(new Adder());
Swipe and inspect that in a an eclipse workspace and you get 6 (1+2+3 for the lazy ML people).

Now the With.all bit was where I started, I liked the feel of that. I didn't persue this much further as you can see the visitor is tortuous to write in in Java. I did however remove all the for and while loops from JUnit and some little refactorings popped out. Yes blocks is a bad name. -- OliBye

  • Anonymous and inner classes didn't improve readability.
  • Strong typing forces casts (yuck the intent is drowning)
  • And as for for the native wrapper classes they should have called them straight jackets. (Argh, they're so wrong)
  • Haven't looked at how this turns out in 1.5 with autoboxing... (I won't hold my breath)



Edit this page   More info...   Attach file...
This page last changed on 28-Sep-2004 10:05:04 BST by 203.125.109.190.