Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which they operate.
PROBLEM
Imagine that your team develops an app which works with geographic information structured as one colossal graph. Each node of the
graph may represent a complex entity such as a city, but also more granular things like industries,sightseeing areas, etc. The
nodes are connected with others if there’s a road between the real objects that they represent. Under the hood, each node type is
represented by its own class, while each specific node is an object.
At some point, you got a task to implement exporting the graph into XML format. At first, the job seemed pretty straightforward.
You planned to add an export method to each node class and then leverage recursion to go over each node of the graph, executing
the export method. The solution was simple and elegant: thanks to polymorphism, you weren’t coupling the code which called the ,
export method to concrete classes of nodes.
Unfortunately, the system architect refused to allow you to alter existing node classes. He said that the code was already in
production and he didn’t want to risk breaking it because of a potential bug in your changes.
Besides, he questioned whether it makes sense to have the XML export code within the node classes. The primary job of these
classes was to work with geodata. The XML export behavior would look alien there.
There was another reason for the refusal. It was highly likely that after this feature was implemented, someone from the
marketing weird stuff. This would , force you to change those precious and fragile classes again.
SOLUTION
The Visitor pattern suggests that you place the new behavior into a separate class called visitor, instead of trying to integrate
it into existing classes. The original object that had to perform the behavior is now passed to one of the visitor’s methods as
an argument, providing the method access to all necessary data contained within the object.
Now, what if that behavior can be executed over objects of different classes? For example, in our case with XML export, the actual
implementation will probably be a little bit different across various node classes. Thus, the visitor class may define not one,
but a set of methods, each of which could take arguments of different types, like this:
class ExportVisitor implements Visitor is
method doForCity(City c) { ... }
method doForIndustry(Industry f) { ... }
method doForSightSeeing(SightSeeing ss) { ... }
But how exactly would we call these methods, especially when dealing with the whole graph? These methods have different
signatures, so we can’t use polymorphism. To pick a proper visitor method that’s able to process a given object, we’d need to
check its class. Doesn’t this sound like a nightmare?
foreach (Node node in graph)
if (node instanceof City)
exportVisitor.doForCity((City) node)
if (node instanceof Industry)
exportVisitor.doForIndustry((Industry) node)
You might ask, why don’t we use method overloading? That’s when you give all methods the same name, even if they support different
sets of parameters. Unfortunately, even assuming that our programming language supports it at all (as Javaand C# do), it won’t
method to execute. It’ll default to the method that takes an object of the base
Node class.
However, the Visitor pattern addresses this problem. It uses a technique calledDouble Dispatch, which helps to
execute the proper method on an object without cumbersome conditionals. Instead of letting the client select a proper version of
the method to call, how about we delegate this choice to objects we’re passing to the visitor as an argument? Since the objects
know their own classes, they’ll be able to pick a proper method on the visitor less awkwardly. They “accept” a visitor and tell
it what visiting method should be executed.
foreach (Node node in graph)
node.accept(exportVisitor)
class City is
method accept(Visitor v) is
v.doForCity(this)
class Industry is
method accept(Visitor v) is
v.doForIndustry(this)