Visitor Design Pattern
Overview
A visitor design pattern is a behavioral design pattern used to decouple the logic/algorithm from the objects on which they operate. The logic is moved to separate classes called visitors. Each visitor is responsible for performing a specific operation.
When Will We Need the Visitor Design Patterns?
We have a list consisting of many shapes (square, circle, rectangle, etc.), and we want to calculate values like area, perimeter of those shapes. The straightforward way is to have area() and perimeter() methods in the respective shape classes. But in this method, we are tightly coupling the algorithm (calculate area, perimeter, etc.) from the objects (square, circle, rectangle, etc.) on which they operate.
With the visitor design patterns, we can remove this tight coupling by separating the algorithms and the objects on which they operate. In the future, if any new algorithms are to be added to the objects, they can be added separately without touching the underlying objects. This leverages the Open/Closed principle (i.e.) classes should be open for extension but closed for modification.
How Do Visitor Design Patterns Work?
We will take the problem mentioned above as an example. We will create a Shape interface and create concrete shape classes such as Square, Circle, and Rectangle. We will then implement the visitor design pattern by defining separate visitor concrete classes to calculate area and perimeter and apply them to the shape objects.
Structure
Visitor design patterns have a few core participants such as Visitor, Concrete Visitors, Element, Concrete Elements, etc.
Visitor
The visitor interface declares a set of visit() methods that take concrete elements as arguments.
E.g.: ShapeVisitor
Concrete Visitor
Concrete Visitors implement the Visitor interface and override the visit() method with respective operations.
Eg: AreaVisitor, PerimeterVisitor.
Element
Element interface declares an accept() method that takes the Visitor interface as an argument.
E.g.: Shape
Concrete Element
Concrete Elements implement the Element interface and override the accept() method. The only purpose of the accept() method is to call the visit() method of the concrete visitor that it takes as the argument.
E.g.: Circle, Square, Rectangle.
Client Client is responsible for creating the concrete visitor instances and passing them to the accept() method of the concrete element instances.
Implementation
- The first step is to create the Shape interface with a single method, accept().
- Create the concrete element classes Square, Circle, and Square that implement the Shape interface.
- Create the ShapeVisitor interface with overloaded visit() methods, each accepting a concrete shape class.
- Update the accept() method of the Shape interface to accept the ShapeVisitor interface as an argument.
- Create the concrete visitor classes AreaVisitor and PerimeterVisitor that implement the ShapeVisitor interface and override all its overloaded visit() methods.
- Finally, create the Client class responsible for creating concrete elements and concrete visitor objects.
Pseudocode
Element
We create the Shape interface with a single method, accept(), that accepts ShapeVisitor as an argument. We will add this code and create a ShapeVisitor interface later.
Pseudocode
Java
Python
C++
Concrete Elements
We create concrete elements Circle, Square, and Rectangle that implements the Shape interface and override the accept() method.
Pseudocode
Java
Python
C++
Visitor
We create the ShapeVisitor interface with visit() methods declared for all the shape classes Square, Circle, and Rectangle.
Pseudocode
Java
Python
C++
Concrete Visitors
We create concrete visitors AreaVisitor and PerimeterVisitor that implements the Visitor interface and overrides all of its visit() methods.
Pseudocode
Java
Python
C++
Client
Finally, we create the Client class responsible for creating the concrete visitor objects and passing them to the concrete element's accept() method.
Pseudocode
Java
Python
C++
Output
Explanation We created a list of Shape objects that contains the shapes Square, Circle, and Rectangle. We also created the AreaVisitor and PerimeterVisitor instances. The shape objects are iterated, and each of their accept() methods is called with the AreaVisitor and PerimeterVisitor. Finally, the result is fetched by calling the respective visitors' get() method.
Pros and Cons of Visitor Design Pattern
Pros
- Adding a new operation to all the Elements can be done easily by adding a new Concrete Visitor that implements the Visitor interface. The Element classes are left untouched.
- Visitors can maintain the state when they visit each element and are encapsulated. The data can be retrieved through a separate public method (get()) provided by the visitors.
Cons
- A new class is added for every new operation performed on the Elements.
- All the visitor classes have to be changed for every new element introduced.
Difference between Visitor Pattern and Strategy Pattern
Visitor Design Pattern | Strategy Design Pattern |
---|---|
Visitor design pattern is used when we want to add operations to a family of classes without touching them directly | Strategy design pattern is used when there are multiple algorithms for the same task, and we want to pick the best one at runtime |
Visitor design pattern helps on maintaining multiple operations for a set of classes | Strategy design pattern helps on maintaining several algorithms for a single operation |
Example: AreaVisitor and PerimeterVisitor are used for different functionalities | Example: CardStrategy and NetBankingStrategy are used for same functionality (i.e) debit money from account |
FAQs
Q: When Should the Visitor Design Pattern Be Used?
A: Visitor design pattern should be used when performing several operations on a set of objects, and we don't want to have logic in those objects.
Example We want to compute area and perimeter on shape objects, and we don't want to have the logic in the shape objects. We can leverage visitor design pattern my adding the logic in separate visitor classes.
Q: What is the Use of Visit and Accept Methods in the Visitor Design Pattern?
A: The visit and accept methods in the visitor design pattern are used to apply logic to the elements. The accept() method is added in the Concrete Element classes, and it accepts the Visitor as an argument. The visit() method is present in the Concrete Visitor classes, containing the operational logic. The visit() method will be called inside the accept() method.