XpdWiki
Set your name in
UserPreferences Referenced by GettersAreEvil
JSPWiki v2.0.52
|
The whipping-boy of patternsThis 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 codingHere' 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 VisitorSo, 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. VisitableThe 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 Faking itIt's a common criticism![]() 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. GoFGoF 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
|