Visitor#

The Visitor design pattern is a way of separating an algorithm from an object structure on which it operates. This pattern is useful when you have a complex object structure and you want to perform operations on these objects without changing their classes.

In layman’s terms, imagine you have a zoo with different types of animals. You want to perform different operations on these animals, like feeding them, cleaning their cages, or giving them medical checkups. Instead of adding these operations to each animal class, you create a Visitor class for each operation. The animals then “accept” a visitor, which performs the operation.

UML Diagram#

+-------------------+       +-------------------+
|    Animal         |       |  AnimalVisitor    |
+-------------------+       +-------------------+
| + accept(visitor) |<------| + visitLion()     |
+-------------------+       | + visitElephant() |
            ^               +-------------------+
            |
    +-------+------------+
    |                    |
+---+-------+        +---+-------+
| Lion      |        | Elephant  |
+-----------+        +-----------+
| + accept()|        | + accept()|
+-----------+        +-----------+

Java Example#

Visitor Design Pattern in Java#
 interface Animal {
     void accept(AnimalVisitor visitor);
 }

 class Lion implements Animal {
     @Override
     public void accept(AnimalVisitor visitor) {
         visitor.visitLion(this);
     }
 }

 class Elephant implements Animal {
     @Override
     public void accept(AnimalVisitor visitor) {
         visitor.visitElephant(this);
     }
 }

 interface AnimalVisitor {
     void visitLion(Lion lion);
     void visitElephant(Elephant elephant);
 }

 class FeedingVisitor implements AnimalVisitor {
     @Override
     public void visitLion(Lion lion) {
         System.out.println("Feeding the lion");
     }

     @Override
     public void visitElephant(Elephant elephant) {
         System.out.println("Feeding the elephant");
     }
 }

 class CleaningVisitor implements AnimalVisitor {
     @Override
     public void visitLion(Lion lion) {
         System.out.println("Cleaning the lion's cage");
     }

     @Override
     public void visitElephant(Elephant elephant) {
         System.out.println("Cleaning the elephant's cage");
     }
 }

 public class Zoo {
     public static void main(String[] args) {
         Animal lion = new Lion();
         Animal elephant = new Elephant();

         AnimalVisitor feedingVisitor = new FeedingVisitor();
         AnimalVisitor cleaningVisitor = new CleaningVisitor();

         lion.accept(feedingVisitor);
         elephant.accept(feedingVisitor);

         lion.accept(cleaningVisitor);
         elephant.accept(cleaningVisitor);
     }
 }

Python Example#

Visitor Design Pattern in Python#
 from __future__ import annotations
 from typing import Protocol

 class AnimalVisitor(Protocol):
     def visit_lion(self, lion: Lion) -> None:
         ...

     def visit_elephant(self, elephant: Elephant) -> None:
         ...

 class Animal(Protocol):
     def accept(self, visitor: AnimalVisitor) -> None:
         ...

 class Lion:
     def accept(self, visitor: AnimalVisitor) -> None:
         visitor.visit_lion(self)

 class Elephant:
     def accept(self, visitor: AnimalVisitor) -> None:
         visitor.visit_elephant(self)

 class FeedingVisitor:
     def visit_lion(self, lion: Lion) -> None:
         print("Feeding the lion")

     def visit_elephant(self, elephant: Elephant) -> None:
         print("Feeding the elephant")

 class CleaningVisitor:
     def visit_lion(self, lion: Lion) -> None:
         print("Cleaning the lion's cage")

     def visit_elephant(self, elephant: Elephant) -> None:
         print("Cleaning the elephant's cage")

 if __name__ == "__main__":
     lion = Lion()
     elephant = Elephant()

     feeding_visitor = FeedingVisitor()
     cleaning_visitor = CleaningVisitor()

     lion.accept(feeding_visitor)
     elephant.accept(feeding_visitor)

     lion.accept(cleaning_visitor)
     elephant.accept(cleaning_visitor)