Visitor design pattern demystified
Today we look at one of the lesser known patterns, often unused and therefore more interesting if used appropriately 😅.
All the examples are written in Typescript… Even if you do not know it, notice that I chose this language because it adds optional static typing to Javascript providing a set of examples easily translatable to non-typed languages (with the appropriate arrangements 😄)
It is one of the patterns of the Gang of Four (GoF — see here), and in particular it is a “Behavioral pattern” that allows separating an algorithm from an object structure on which it operates. This means that we can add new operations to this object structure (even a list of heterogeneous classes) without modifying it.
Let’s try to describe a simple real-life example (you can play with it— see this Github repo written using Typescript) where we have a list of Employee
and Clerk
objects on which we need to perform two actions (and maybe others in future):
- increase income: increase the income of the
Employee/Clerk
by 30% and 10% respectively - increase vacation days: increase the vacation by 3 days for the
Employee
and by 1 day for theClerk
First we declare the IVisitor
interface that exposes the visit method: This method will be suitably defined by the two visitor implementations regarding the actions above, ie. IncomeVisitor
and VacationVisitor
.
In this case, we distinguish the type Clerk
from the type Employee
with a simple if(item instanceof MyType)
doing the operations according to the above specifications: notice this is a simplified variation of the visitor pattern that can (or should 😏) provide separate methods for different types, see a second implementation here.
Let’s proceed by adding the second part of the pattern:
We declare a new interface IVisitable
that exposes the accept method: this method allows us to “receive” a visitor instance and perform the action appropriately on the interested item.
This allows us to specify a different behavior for different types of objects / structures: in this case it we specify that in the case of a Employee list, the accept method must be executed on each component of the list.
Here a simple example that include all the code seen so far:
Summary
The visitor pattern can be summed up in:
Goal
- Separate algorithms from an object structure on which them operate. This allows us to add new operations to this object structure without modifying it
Elements
- IVisitor interface providing a visit method (for each visitable class)
- IVisitable interface providing an accept method, implemented by every visitable class
Pros
- Add functions to class libraries for which you either do not have the source or cannot change the source
- Collect data from a disparate collection of unrelated classes and use it to present the results of a global calculation to the user program
- Gather related operations into a single class rather than force you to change or derive classes to add these operations
- Collaborate with the Composite pattern
Cons
- Adding or removing visitables requires you to update all visitors
- “Visited classes” need to be “stable”
UML
We can summarize the visitor pattern with the following sentence:
Implement as many visitor classes as the actions to be performed on a set of objects by specifying the method of accepting each visitor instance for each object type.
Have fun and do not forget to recommend the article if you liked it 👏!