Back to: Java Design Patterns
Visitor Design Pattern in Java with Examples
In this article, I am going to discuss the Visitor Design Pattern in Java with Examples. Please read our previous article where we discussed the Null Object Design Pattern in Java with Examples. The Visitor Design Pattern falls under the category of Behavioral Design Pattern. In this article, we will explore the Visitor Design Pattern in Java, exploring its advantages, disadvantages, and practical applications in software development.
What is Visitor Design Pattern?
In the realm of software design patterns, the Visitor Design Pattern stands out as a powerful tool for separating algorithms from the objects they operate on. By enabling the addition of new operations without modifying the object structure, the Visitor Design Pattern promotes extensibility, maintainability, and code readability.
The Visitor Design Pattern is a behavioral design pattern that separates the algorithms or operations from the objects on which they operate. It allows new operations to be added to existing object structures without modifying those objects. This pattern achieves its goal by introducing a visitor object that encapsulates the algorithm and interacts with the objects in the structure.
The visitor object defines multiple visit methods, each corresponding to a specific object type in the structure. The objects in the structure accept the visitor, and in turn, the visitor applies the appropriate algorithm by invoking the corresponding visit method on each object. This decoupling of the algorithm from the object structure enables new operations to be added simply by introducing new visitor subclasses.
Real-Time Example to Understand Visitor Design Pattern:
In the Visitor Design Pattern, we use a Visitor object which changes the executing algorithm of an element object. In this way, when the visitor varies, the execution algorithm of the element object can also vary. As per the Visitor Design Pattern, the element object has to accept the visitor object so that the visitor object handles the operation on the element object.
Please have a look at the following image. Here, we have a school and in school, lots of kids are studying. One day the school management decided to perform a health checkup for all the kids. So, what the school management has done is appoint one child specialist doctor. What the doctor will do is, he will visit the school and check the health of each and every kid. Once he has checked the health of each kid, then he provides the reports to the school management.
So, here in this example, the doctor is nothing but a visitor and the object structure is the collection of Kids where each kid is an element object.
In the same manner, one school bag sales company wants to promote its school bag. So, the company communicates with the school management and decided to give a school bag to each kid as a gift. So, the company salesman visits the school and gives a school bag to each kid. For a better understanding please have a look at the following diagram.
Here, the salesman is the visitor and the object structure is the same collection of kids where each kid is an element.
Implementing Visitor Design Pattern in Java:
A real-world example where the Visitor Design Pattern can be applied effectively is in a clothes shop. Consider a shop that sells different types of clothes, such as shirts, pants, and shoes. Each type of cloth may have its own structure and elements, and the system needs to perform various operations on these documents, such as setting their price.
By utilizing the Visitor Design Pattern, the system can define a visitor interface or base class that declares visit methods for each type of cloth. The concrete visitor classes implement these visit methods and provide specific operations or algorithms for processing each type of cloth.
This approach allows new operations to be added to the clothes shop without modifying the classes. For example, a new visitor class can be created to generate a bill.
The Visitor Design Pattern enables a modular and flexible approach. New cloth types can be easily added by introducing new classes that implement the visitor interface and defining their specific visit methods. The pattern separates the clothes from the operations performed on it, promoting code reusability, maintainability, and extensibility. The UML Diagram of this example is given below using Visitor Design Pattern.
Step 1: Create a new directory to store all the class files of this project.
Step 2: Open VS Code and create a new project, called visitor.
Step 3: In the project, create a new file called Cloth.java. Add the following code to the file:
public interface Cloth { public void accept(ClothVisitor c); }
This is the interface from which other concrete classes will extend.
Step 4: In the project, create four new files called Shirt.java, Pant.java, Shoes.java, and Outfit.java. All four of these files extend from the Cloth interface. Add the following code to the files:
Shirt.java
public class Shirt implements Cloth { @Override public void accept(ClothVisitor c) {c.visit(this);} }
Pant.java
public class Pant implements Cloth { @Override public void accept(ClothVisitor c) {c.visit(this);} }
Shoes.java
public class Shoes implements Cloth { @Override public void accept(ClothVisitor c) {c.visit(this);} }
Outfit.java
public class Outfit implements Cloth { private Cloth[] clothes; public Outfit() { clothes = new Cloth[] {new Shirt(), new Pant(), new Shoes()}; } @Override public void accept (ClothVisitor c) { for (int i = 0; i < clothes.length; i++) clothes[i].accept(c); c.visit(this); } }
Step 5: In the project, create a new file called ClothVisitor.java. This interface is responsible for defining the methods which will be implemented in the concrete visitor class. Add the following code to ClothVisitor.java:
public interface ClothVisitor { public void visit (Outfit o); public void visit (Shirt s); public void visit (Pant p); public void visit (Shoes s); }
Step 6: Create a class called ClothVisitorImpl.java. This class implements the ClothVisitor.java interface. Add the following content to the ClothVisitorImpl.java:
public class ClothVisitorImpl implements ClothVisitor { @Override public void visit(Outfit o) {System.out.println("Displaying outfit!");} @Override public void visit(Shirt s) {System.out.println("Displaying shirt!");} @Override public void visit(Pant p) {System.out.println("Displaying pant!");} @Override public void visit(Shoes s) {System.out.println("Displaying shoes!");} }
Step 7: In the project, create a new file called VisitorPatternDemo.java. This class will contain the main() function. Add the following code to VisitorPatternDemo.java:
public class VisitorPatternDemo { public static void main(String[] args) {new Outfit().accept(new ClothVisitorImpl());} }
Step 8: Compile and execute the application. Ensure compilation is successful. Verify that the program works as expected.
Congratulations! You now know how to implement visitor patterns!
UML Diagram of Visitor Design Pattern:
Now, let us see the Visitor Design Pattern UML Diagram Components with our Example so that you can easily understand the UML Diagram.
The classes can be described as follows:
- Object: This is the interface that will define the basic methods that will be implemented by the concrete object class.
- ConcreteObject: This class implements the Object interface. It implements the functions defined in the interface.
- Visitor: This interface defines the method that needs to be implemented by the concrete visitor class.
- VisitorImpl: This is the concrete interface class that implements all the methods from the visitor interface.
- DriverClass: This class contains the main() function and is responsible for the simulation of the program.
Advantages of Visitor Design Pattern in Java
The advantages of using the Visitor Design Pattern in Java are as follows:
- Separation of Concerns: The Visitor Design Pattern promotes the separation of concerns by isolating algorithms in visitor objects. The objects in the structure only need to accept the visitor, without being aware of the specific operations performed on them. This separation enhances code readability, maintainability, and modularity, as each class focuses on its own responsibilities.
- Extensibility and Open/Closed Principle: The Visitor Design Pattern adheres to the Open/Closed Principle, allowing new operations to be added without modifying the existing object structure. By introducing new visitor subclasses, developers can extend the functionality of the object structure, making it more flexible and adaptable to changing requirements. This extensibility reduces the risk of introducing bugs and simplifies future enhancements.
- Double Dispatch: The Visitor Design Pattern leverages the concept of double dispatch, where the visitor’s visit method is determined at runtime based on both the visitor object and the object being visited. This dynamic binding enables polymorphic behavior, as the appropriate visit method is invoked based on the type of object being visited. This flexibility allows for complex operations that depend on both the visitor and the visited object’s types.
- Code Reusability: The Visitor Design Pattern promotes code reusability by encapsulating operations within visitor objects. Visitors can be reused across different object structures, providing a convenient way to apply the same algorithm to various sets of objects. This reusability reduces code duplication, enhances maintainability, and improves development efficiency.
- Data Gathering and Analysis: The Visitor Design Pattern is particularly useful for data gathering and analysis scenarios. By defining different visitor subclasses, each responsible for collecting specific data from objects, complex data analysis tasks can be performed seamlessly. This pattern allows for the aggregation of information from multiple objects, making it easier to extract meaningful insights and perform statistical analyses.
Disadvantages of Visitor Design Pattern in Java
The disadvantages of using the Visitor Design Pattern in Java are as follows:
- Increased Complexity: The Visitor Design Pattern introduces additional classes and abstractions, which can increase the complexity of the codebase. Understanding the flow of control between objects and visitors can be challenging, especially for developers who are new to the codebase. Care must be taken to balance the benefits of flexibility and extensibility with the potential complexity introduced by the pattern.
- Dependency Inversion Principle Violation: The Visitor Design Pattern can violate the Dependency Inversion Principle, as it requires the objects in the structure to depend on the visitor interface or base class. This dependency can lead to increased coupling between the objects and the visitors, potentially impacting the maintainability and flexibility of the codebase.
- Limited Encapsulation: The Visitor Design Pattern can break encapsulation, as the visit methods in the visitor objects require access to the internal state of the visited objects. While this may be necessary to perform the desired operations, it can expose internal details and violate the principle of encapsulation, potentially leading to less modular and more tightly coupled code.
- Runtime Performance Overhead: The Visitor Design Pattern may introduce a slight runtime performance overhead due to the dynamic binding and multiple method invocations involved. This overhead can be negligible for small object structures but may become a concern for larger and more complex systems.
In the next article, I am going to discuss MVC Design Pattern in Java with Examples. Here, in this article, I try to explain Visitor Design Pattern in Java with Examples. I hope you understood the need for and use of the Visitor Design Pattern in Java.