I used the Visitor example given here Where we have this:
.------------------------.
| Flower |
+------------------------+
| +accept(visitor) |
| +pollinate(pollinator) |
| +eat(eater) |
'------------------------'
We also have a Bug
and a Bee
that can pollinate
a Flower
, and a Predator
that can eat
a flower.
Using the vistor pattern I can write this:
bee = Bee()
fly = Fly()
worm = Worm()
# Using the visitor pattern:
for flower in flowerGen(10):
for object in [bee, fly, worm]:
flower.accept(object)
But the code is as readable and funcitonal without the visitor:
# Without visitor pattern
for flower in flowerGen(10):
for object in [bee, fly, worm]:
object.visit(flower)
The question is, what advantages provide the Visitor Pattern in this example?
The article you link to is pretty clear as to why you'd want to use a visitor pattern: when you can't alter the objects because they come from a third party:
The assumption is that you have a primary class hierarchy that is fixed; perhaps it’s from another vendor and you can’t make changes to that hierarchy. However, your intent is that you’d like to add new polymorphic methods to that hierarchy, which means that normally you’d have to add something to the base class interface. So the dilemma is that you need to add methods to the base class, but you can’t touch the base class. How do you get around this?
Sure, if you can just add a visit
method to bees, flies and worms, then that's fine. But when you can't, using the visitor pattern is the next best option.
Note that in the article the relationship is reversed; you can't alter the Flower
hierarchy:
# The Flower hierarchy cannot be changed:
but the class does support the visitor dispatch pattern via the visit
method:
class Flower(object):
def accept(self, visitor):
visitor.visit(self)
That implementation could be much more complex; the example has been simplified down to a simple visitor.visit()
call here, but in practice a real visitor pattern can and does do much more at this stage.
For example, there could be composite classes, which contain multiple subcomponents. The accept()
method would then delegate further down to those sub-elements to then call accept
on them all, as needed. Keeping with the flower theme, perhaps there's a Chrysanthemum or Dahlia class, where some visitors would eat the ray components, while others would like to visit the components in the eye to pollinate. It's up to the composite object to direct each visitor to those parts individually.
If you are looking for specific examples, take a look at the ast
module, which offers a NodeVisitor
class which should be subclassed to add methods to let you customise how the AST tree passed in is being processed. I've used the specific NodeTransformer
subclass to alter how Python code works on several occasions. Here the visitor pattern is used to effectively filter out certain types in a larger hierarchy, greatly simplifying AST-handling code without having to alter any of the AST node classes themselves.