Visitor pattern: explanations and examples
Object-oriented programming (OOP), which is considered a subfield of imperative programming, has taken on a lot of meaning over the last few years. The option to define all components of a software project as objects that behave differently depending on their class has some distinct advantages over other types of programming – a big one is the ability to easily reuse already coded parts of the program. For developers, this is one of the strongest arguments in favor of OOP.
This capacity for reuse, as well as tips on how to make implementing, adapting, and testing created objects even easier, was all explained in the book Design Patterns: Elements of Reusable Object-Oriented Software published by the “Gang of Four” developers. One of the over 20 different design patterns in this book is the “visitor pattern” or “visitor design pattern”, which we will explain in detail over the next sections.
What is a visitor pattern?
The visitor pattern or visitor design pattern is a pattern that will separate an algorithm from the object structure on which it operates. It describes a way to add new operations to existing object structures without modifying the structures themselves. This characteristic makes visitor patterns a way to implement the open/closed principle (OCP). This principle of object-oriented software development states that each software entity – such as modules, classes or functions – should be simultaneously open for extension but closed for modification.
The visitor pattern is one of the 23 design patterns (within the category of behavioral patterns) described and published by the computer scientists Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides in 1994. Collectively, these developers are known as the “Gang of Four” (or GoF for short), which led to these patterns being referred to as the GoF design patterns.
What does the visitor design pattern do?
If an object structure is made of several, unrelated classes, it’s highly inconvenient for developers to have to create a new subclass for each operation if they happen to need new operations often. The result of coding like this would be a system with a host of different entangled classes that is not only difficult to understand, but also hard to manage and edit if necessary. The biggest advantage of a visitor pattern is that the “visitor” lets you add new, virtual functions to a class family without having to modify any of the classes themselves.
Virtual functions (or methods) define target functions for which the target may be unknown at the time of coding. They are an important tool in object-oriented systems.
A visitor design pattern lets a “visitor object” be defined separately, with the goal of implementing an operation to be performed on one or several elements of the object structure. Clients that access the object structure call the dispatching operation accept(visitor) on an element that delegates the request to the accepted visitor object. The visitor object can then perform that operation on the element.
Graphical example of a visitor pattern (UML diagram)
The connection between available elements and linked visitor objects using a visitor design pattern is best demonstrated graphically in order to depict all relationships and exchanges in a theoretical object-oriented system. The best set-up for this purpose is the Unified Modeling Language (UML), which we chose to use in the following class diagrams for our visitor pattern.
The pros and cons of the visitor pattern
A visitor pattern is a pre-made, well-functioning way to extend already existing entities in an object-oriented system. Adding a new operation can be done easily by simply defining the new visitor. This also allows all functional code to be centralized. Implementing any new operations is done centrally in the visitor class only, and the rest of the classes don’t need to be touched. The biggest advantage of a program using a visitor design pattern can be boiled down to the fact that the existing source code doesn’t have to be constantly adapted to new objects used. Instead, the logic is split between the visitors and visitor classes that act as stand-ins.
Of course, visitor design patterns are not perfect in all respects. Anyone wanting to work with this pattern needs to bear in mind that even slight changes to an element class generally mean that related visitor classes also have to be adjusted. There’s no way around the extra work when introducing new elements at a later date, as they require the addition of visit() methods which in turn have to be added into the ConcreteVisitor classes. The fantastic possibilities for software entity extensions are therefore linked to a non-negligible amount of effort.
How to use a visitor pattern
A visitor pattern can considerably simplify recurring tasks in software development. Developers who work with object-oriented systems should definitely consider reckoning with this design pattern. Since its design in 1994, the visitor pattern has become a big player in the programming scene, and it can prove useful regardless of the software project at hand. There are also no real restrictions in terms of the programming language you want to use, as long as you remember that it works best in object-oriented paradigms.
Popular languages for which visitor patterns can play an important role include:
- C++
- C#
- Java
- PHP
- Python
- JavaScript
- Golang
Practical example of how to use a visitor pattern
We appreciate that it’s not that easy to understand the use and point of a visitor pattern, but anyone learning to code today will certainly come into contact with this pattern.
For a simple-to-grasp analogy from a real-world situation, visitor patterns are often demonstrated using cab rides. Let’s assume that a customer calls a cab to take them to their front door. The customer gets into the “visiting car”, but it’s the cab (or rather, the driver) that is fully in control of transporting the person.
Shopping in a supermarket is another common example to explain how visitor patterns work. The person doing their grocery shopping puts everything they want in their shopping cart, which can be considered a set of elements from the object structure. The cashier then acts like a visitor, scanning prices and/or weighing each individual shopping item (i.e., element) to calculate the total cost.
Example code for a visitor pattern (PHP)
The following code is a simple, basic use of a visitor pattern in PHP.
return 'B';
}
public function getData() {
return $this->the_data;
}
public function accept(Visitor $visitor) {
$visitor-> VisitFromElementB($this);
}
}
abstract class Visitor {
abstract function VisitFromElementA(ElementA $elem);
abstract function VisitFromElementB(ElementB $elem);
}
class Visitor1 extends Visitor {
private $characteristics;
public function getCharacs() {
return $this->characteristics;
}
public function VisitFromElementA(ElementA $elem) {
$this->characteristics = 'Info:'.$elem->getInfo();
}
public function VisitFromElementB(ElementB $elem) {
$this->characteristics = 'DATA:'.$elem->getData().'!!';
}
}
function Test() {
write_line('Teststart');
// Objectstructure
$elements = array (
new ElementA('Hello', 'New!!'),
new ElementB('Finally.'),
);
$vis1 = new Visitor1();
foreach ($elements as $element) {
$element->accept($vis1);
write_line('After visit from Element '.$element->getName().': '.$vis1- >getCharacs());
}
}
function write_line($text) {
print $text.'<br>';
}
Test();
The output for this example code snippet is as follows:
Teststart
After visit from Element A: Info:[Hello--New!!]
After visit from Element B: DATA:(Finally.)!!