What is the Visitor Design Pattern?
Double dispatching can be useful if some parts of the class do not really fit there
The idea behind the visitor design pattern is to put parts of the code that have specific responsibilities outside of the class.
The most common use case for the pattern is reporting and exporting data to a given format. Those are very useful features that can easily get complex. They operate on data stored in an object but have very specific tasks to perform.
The single responsibility principle states that a class should have only one reason to change. If we want to add reporting or exporting on a structure of data, we will have at least two responsibilities.
This would not be good practice. Moreover, using the visitor pattern, we can add new features to the visitor and visitee independently, so our code is in line with the open/closed principle too.
Double dispatch is a concept very closely correlated with the Visitor. It occurs when the behavior of a function is dependent on both callers and called classes.
We will have different results and behavior in the visited
Company class when we use it with
PDFExporter visitors. In spite of the fact that both visitor classes share the same public interface.
We will be performing quite a straightforward task. There is a problem with students missing classes in a university. We need to add a function for calculating days missed due to sick leave for each student.
It seems like a job for the visitor pattern. Let’s start by defining the interface of classes that can be visited and visitors.
Each visitable class will have to implement the
accept method. Depending on the type of class, the implementation will be slightly different.
Our student models are very simple and consist only of a name and a list of sick leaves.
Each sick leave has a start and end date. It will make operating on those much easier.
SickLeave class would also be a good place for validation of input data (e.g. if the start date is smaller than the end date) but we want to keep this example as simple as possible.
The University also has a very straightforward model. It consists only of a name and a list of students.
To generate data about missed days for each student, we need to calculate the number of days in each sick leave and format it into readable messages.
Our report is ready and we kept our code clean. If we wanted to add a report about exam scores, demographics, or any other data, we can easily reuse the created interface and make no changes to
Another possibility to implement such behavior without modifying the source class would be through inheritance.
We can have a
StudentSickReport class that will be responsible for report generation but it will also have a lot of overhead of unnecessary logic from
Student that it should not care or know about.
A Visitor Has the Following Elements
- Visitor (
Visitor) — Defines the interface of visitors.
- Concrete Visitor (
SickLeaveReport) — Concrete implementation of operations defined in the
- Element (
Visitable) — Defines the “accept” operation interface, taking
Visitoras an argument.
- Concrete element (
University) — Concrete implementation of elements.
The visitor pattern should be used in stable classes. If the visited class often changes, it may force changes on visitors and the pattern becomes problematic to maintain.
It is definitely not the most popular design pattern as it has a relatively narrow range of use cases but it still can be very handy.
- Additional operations can be added easily without modification of the source class.
- Promotes single responsibility and open/closed principles.
- Can over-complicate code in some cases.
- Not suitable for unstable classes.
Full source code and some other patterns are available here: