Apr 9th, 2023
Visitor Design Pattern is one of the behavioral design patterns that separates the algorithm from the objects on which it operates. The Visitor pattern is used when we have to perform an operation on a group of objects that are unrelated to each other.
In this pattern, we define a Visitor class that defines the operation that we want to perform on an object structure. Then we create concrete classes derived from the Visitor class that implement these operations.
Let's dive into an example to understand it better.
Example in Java:
Let's consider an example where we have a hierarchy of animals, and we want to perform an operation on them.
First, let's create an interface that will define the method that will accept the Visitor.
public interface Animal { void accept(AnimalVisitor visitor); }
Next, let's create some concrete classes that will implement the Animal interface.
public class Lion implements Animal { @Override public void accept(AnimalVisitor visitor) { visitor.visit(this); } } public class Bear implements Animal { @Override public void accept(AnimalVisitor visitor) { visitor.visit(this); } }
Now, let's create the Visitor interface that defines the operations we want to perform.
public interface AnimalVisitor { void visit(Lion lion); void visit(Bear bear); }
Next, let's create the concrete classes that will implement the AnimalVisitor interface.
public class FeedingVisitor implements AnimalVisitor { @Override public void visit(Lion lion) { System.out.println("Feeding Lion"); } @Override public void visit(Bear bear) { System.out.println("Feeding Bear"); } } public class PlayingVisitor implements AnimalVisitor { @Override public void visit(Lion lion) { System.out.println("Playing with Lion"); } @Override public void visit(Bear bear) { System.out.println("Playing with Bear"); } }
Now, let's create a class that will use the Visitor pattern to perform operations on the Animal objects.
public class AnimalOperations { public void performOperation(Listanimals, AnimalVisitor visitor) { for (Animal animal : animals) { animal.accept(visitor); } } }
Let's see how we can use this pattern to perform the operation.
public class Main { public static void main(String[] args) { Listanimals = new ArrayList<>(); animals.add(new Lion()); animals.add(new Bear()); AnimalOperations animalOperations = new AnimalOperations(); AnimalVisitor feedingVisitor = new FeedingVisitor(); AnimalVisitor playingVisitor = new PlayingVisitor(); animalOperations.performOperation(animals, feedingVisitor); animalOperations.performOperation(animals, playingVisitor); } }
Output:
Feeding Lion Feeding Bear Playing with Lion Playing with Bear
Example in C#:
Let's implement the same example in C#.
First, let's create an interface that will define the method that will accept the Visitor.
public interface IAnimal { void Accept(IAnimalVisitor visitor); }
Next, let's create some concrete classes that will implement the IAnimal interface.
public class Lion : IAnimal { public void Accept(IAnimalVisitor visitor) { visitor.Visit(this); } } public class Bear : IAnimal { public void Accept(IAnimalVisitor visitor) { visitor.Visit(this); } }
Now, let's create the Visitor interface that defines the operations we want to perform.
public interface Visitor { void visit(Book book); void visit(CD cd); void visit(DVD dvd); }
We have defined three visit methods, one for each type of item in our library. Now, let's create concrete visitor classes that implement these operations:
public class DisplayVisitor implements Visitor { @Override public void visit(Book book) { System.out.println("Displaying Book: " + book.getTitle() + ", by " + book.getAuthor()); } @Override public void visit(CD cd) { System.out.println("Displaying CD: " + cd.getTitle() + ", by " + cd.getArtist()); } @Override public void visit(DVD dvd) { System.out.println("Displaying DVD: " + dvd.getTitle() + ", directed by " + dvd.getDirector()); } } public class RentVisitor implements Visitor { @Override public void visit(Book book) { System.out.println("Renting Book: " + book.getTitle() + ", by " + book.getAuthor()); } @Override public void visit(CD cd) { System.out.println("Renting CD: " + cd.getTitle() + ", by " + cd.getArtist()); } @Override public void visit(DVD dvd) { System.out.println("Renting DVD: " + dvd.getTitle() + ", directed by " + dvd.getDirector()); } }
Here, we have defined two concrete visitor classes: DisplayVisitor and RentVisitor. The DisplayVisitor class implements the visit methods to display the details of the items in the library, while the RentVisitor class implements the visit methods to rent the items in the library.
Finally, let's see how we can use the visitor pattern to perform these operations on our library:
public class Library { private List- items; public Library() { items = new ArrayList<>(); } public void addItem(Item item) { items.add(item); } public void accept(Visitor visitor) { for (Item item : items) { item.accept(visitor); } } } public class Main { public static void main(String[] args) { Library library = new Library(); library.addItem(new Book("The Great Gatsby", "F. Scott Fitzgerald")); library.addItem(new CD("Thriller", "Michael Jackson")); library.addItem(new DVD("The Godfather", "Francis Ford Coppola")); library.addItem(new Book("To Kill a Mockingbird", "Harper Lee")); Visitor displayVisitor = new DisplayVisitor(); Visitor rentVisitor = new RentVisitor(); System.out.println("Displaying items:"); library.accept(displayVisitor); System.out.println("\nRenting items:"); library.accept(rentVisitor); } }
In the Main class, we create a new Library object and add some items to it. Then, we create two visitors: displayVisitor and rentVisitor. We first use the displayVisitor to display the details of the items in the library, and then use the rentVisitor to rent the items.
The output of the program is as follows:
Displaying items: Displaying Book: The Great Gatsby, by F. Scott Fitzgerald Displaying CD: Thriller, by Michael Jackson Displaying DVD: The Godfather, directed by Francis Ford Coppola Displaying Book: To Kill a Mockingbird, by Harper Lee