Photo by Daryan Shamkhali on Unsplash
Visitor Design Pattern explained in 2 minutes
Practical guide to explain Visitor Design Pattern
Table of contents
Problem Statement
Consider the below Animal
interface. If you want to add new methods to the interface, you have to update Cow
and Dog
classes to implement those methods.
public interface Animal {
}
public class Cow implements Animal {
}
public class Dog implements Animal {
}
But what if you want to add new functionalities across all the classes without worrying about changing them, then Visitor Design Pattern is perfect for you.
public interface Animal {
<T> T accept(AnimalVisitor<T> visitor);
}
public class Cow implements Animal {
@Override
public <T> T accept(AnimalVisitor<T> visitor) {
return visitor.visit(this);
}
}
public class Dog implements Animal {
@Override
public <T> T accept(AnimalVisitor<T> visitor) {
return visitor.visit(this);
}
}
In the above scenario, let's say you want to add new functionality like Speak
or NumberOfLegs
, then you have to create an implementation of AnimalVisitor
and implement your business logic directly in those classes.
public interface AnimalVisitor<T> {
T visit(Cow cow);
T visit(Dog dog);
}
public class LegsVisitor implements AnimalVisitor<Integer> {
@Override
public Integer visit(Cow cow) {
return 4;
}
@Override
public Integer visit(Dog dog) {
return 4;
}
}
public class SpeakVisitor implements AnimalVisitor<String> {
@Override
public String visit(Cow cow) {
return "Moo";
}
@Override
public String visit(Dog dog) {
return "Bark";
}
}
Driver Code
public class Main {
public static void main(String[] args) {
Animal cow = new Cow();
Animal dog = new Dog();
AnimalVisitor<Integer> legsVisitor = new LegsVisitor();
AnimalVisitor<String> speakVisitor = new SpeakVisitor();
System.out.println(cow.accept(legsVisitor));
System.out.println(cow.accept(speakVisitor));
System.out.println(dog.accept(legsVisitor));
System.out.println(dog.accept(speakVisitor));
}
}
Output:
4
Moo
4
Bark
What's the benefit?
If you refer to the code once more, then you will notice that we added new functionality to
Cow
andDog
class without changing it -- this functionality is critical when working with client libraries.All the business logic is encapsulated in Visitor classes, which can help in segregating domain logic from model classes e.g.
LegsVisitor
contains all the logic for calculating the number of legs for all types of animals.Adherence to Open-Closed Principle i.e. class is open to extension but closed for modifications.
Adherence to the Single Responsibility Principle i.e. class has only one responsibility.
What's the drawback?
If you have a lot of sub-classes, then the Visitor implementation must handle them even if you want to add new functionality to only one of the classes.
While adding a new class, you must update the existing visitor implementations to support the new class.