this week we let go of all that algebraic stuff and concentrate a bit more on
what object oriented programming is all about. Java claims to support OO, so
why not use it? In this week's article we're going to talk a bit about when
and why to apply certain patterns. We'll start with the Visitor pattern.
The pattern is also named 'double dispatch' which will become clear near the
end of this little article. Here's the story:
Suppose some time ago you wrote a bunch of beautiful, efficient classes; they're
real gems. For the sake of this article let's use the following classes:
Expand|Select|Wrap|Line Numbers
- public class Beautiful { ... }
- public class Efficient { ... }
- public class Gem { ... }
functionality to them; users always use your class objects in collections; they
traverse the collections and call the fine functionality of the objects in those
collections. An ideal solution.
Until one sad day a customer shows up, loaded with money and willing to pay and
he wants some additional functionality added to your classes. He already uses
your classes and he's very happy with them: he thinks they're very efficient,
beautiful and real gems. but ... he wants a bit of functionality added to them.
Let's call this customer Fred.
You do like the money Fred is willing to pay and you start adding the wanted
functionality and you're doing quite fine until the next sad day Barney comes
in at your office and he wants some additional functionality too. Above all
he's willing to pay the money for it also. Happy to oblige you tell Barney
that you're working on additional functionality for customer Fred too at that
very moment and you're willing to accept the task of course.
Barney isn't charmed by the idea: he doesn't like Fred's added functionality
in his version of the updated classes at all and he isn't willing to pay for
it either. You decide that two different versions of your efficient, beautiful
gems of classes isn't that bad and the money isn't bad either.
That night you've got a nightmare: Wilma, Betty, Pebbles, Bambam and even Dino
enter your office and they all want some additional functionality added to your
fine classes too. A maintenance nightmare rears its ugly head.
What to do? You don't want seven versions of your classes and maybe entire
Bedrock wants additional functionality added to your classes, all different.
Then you wake up and find the solution: instead of implementing more and more
versions of your fine classes you do this:
Expand|Select|Wrap|Line Numbers
- public class Beautiful implements Visitee {
- public void whoIsThere(Visitor v) { v.thisIsBeautiful(this); }
- // ...
- }
- public class Efficient implements Visitee {
- public void whoIsThere(Visitor v) { v.thisIsEfficient(this); }
- // ...
- }
- public class Gem implements Visitee {
- public void whoIsThere(Visitor v) { v.thisIsGem(this); }
- // ...
- }
Expand|Select|Wrap|Line Numbers
- public interface Visitee {
- public void whoIsThere(Visitor v);
- }
- public interface Visitor {
- public void thisIsBeautiful(Beautiful b);
- public void thisIsEfficient(Efficient e);
- public void thisIsGem(Gem g);
- }
you know and you only added one little method to each class and you've defined
two interfaces. That's all you did.
Let's implement the functionality for customer Fred; you still have to implement
it because you've accepted the money, but now you can implement the functionality
in a separate class: the Fred class; you give it an Iterator over all the objects
in Fred's collection and implement the functionality like this:
Expand|Select|Wrap|Line Numbers
- public class Fred implements Visitor {
- private Iterator<Visitee> i;
- public Fred(Iterator<Visitee> i) { this.i= i; }
- public void especiallyForFred() {
- while (i.hasNex()) i.whoIsThere(this);
- }
- public void thisIsBeautiful(Beautiful b) {
- // added functionality for Beautiful
- // all specially made for Fred
- }
- public void thisIsEfficient(Efficient e) {
- // added functionality for Efficient
- // all specially made for Fred
- }
- public void thisIsGem(Gem g) {
- // added functionality for Gem
- // all specially made for Fred
- }
- }
with an Iterator over his collections and then call the especiallyForFred
method. He gets his additional functionality then and there's no need for
him (or his programmers) to do anything else.
I think you can imagine what Barney's class will look like. If Wilma, Betty,
Bambam, Pebbles and even Dino show up you don't have to alter your fine classes;
all you have to do is implement the functionality they want, all in a separate
class, just as we did in the example above. Each customer has his own added
functionality and neither functionality clashes with other functionalities
(you simply don't pass them along to other customers). All that's in your
maintenance list now are those separate Visitor classes, one for each customer).
This is what the Visitor pattern is all about: a fixed set of classes or
interfaces all used in a fixed manner and additional functionality that has
to be implemented for different situations. Your fixed set of classes will be
the Visitees and the aditional functionalities will be implemented by the visitors.
The modifications needed for the original fine classes are minor (just one
little method added that implements the Visitee interface).
Of course the additional functionality still needs to be implemented but you've
drawn that away from your original set of classes. And even more: you can add
whatever additional functionality on demand without any modification to your
original class ad nauseam. On the other hand: the Visitor pattern can only be
applied when your set of classes is more or less fixed (you don't want to change
those interfaces and previously existing Visitors again and again). But if your
set of classes is fixed, go for it: implement Visitors in your existing code
base and keep your classes clean. You just have to prepare your classes for
Visitors that may not even have been designed and written yet, i.e. your fixed
set of classes is prepared for an unknown future.
If you carefully read the example classes and interfaces above you'll notice
that the Visitor starts by calling a Visitee; the Visitee responds by calling
the appropriate method on the Visitor again. That's the reason why the Visitor
pattern is dubbed the 'Double Dispatch' pattern: from Visitor to Visitee and
back again. That's all that's needed to keep your classes clean and frees you
from maintenance nightmares.
See you next time when we talk a bit about Decorators (also called Wrappers)
and what they can do for you.
kind regards,
Jos