Design pattern "Visitor"

Let's learn what is the "Visitor" design pattern πŸ€πŸ‘‹πŸšΆπŸ§³
Wednesday, August 14, 2024

Real world example

In plain words

Visitor pattern lets you add further operations to objects without having to modify them.

Wikipedia definition

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle.

Programmatic example

import java.util.ArrayList;
import java.util.List;
interface Shape {
public void accept(Visitor visitor);
}
class Circle implements Shape {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Square implements Shape {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
interface Visitor {
public void visit(Circle circle);
public void visit(Square square);
}
class RenderVisitor implements Visitor {
public void visit(Circle circle) {
System.out.println("Rendering a circle.");
}
public void visit(Square square) {
System.out.println("Rendering a square.");
}
}
public class Main {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle());
shapes.add(new Square());
Visitor visitor = new RenderVisitor();
for (Shape shape : shapes) {
shape.accept(visitor);
}
}
}

Problem it Solves

  • Imagine you have an object structure (like the Shape hierarchy in the example) with various classes (e.g., Circle, Square).
  • You want to add new operations (e.g., rendering, calculating area) to these classes without modifying their code directly.
  • Adding new subclasses for each operation can lead to inflexibility and maintenance challenges.

Solution

  • The Visitor pattern defines a separate visitor object that implements an operation to be performed on elements of the object structure.
  • Clients traverse the object structure and call a dispatching operation (accept) on an element, which delegates the request to the visitor.
  • The visitor then performs the operation on the element (i.e., β€œvisits” the element).

Benefits

  • Open/Closed Principle: You can add new operations without changing the existing classes.
  • Separation of Concerns: Algorithms are isolated from the object structure, promoting better organization and maintainability.
  • Plug-and-Play: You can plug in new visitors without modifying the source code of the elements.

In the example:

  • RenderVisitor implements the Visitor interface with methods for rendering circles and squares.
  • When you iterate through the list of shapes and call accept(visitor), the appropriate visit method is invoked based on the shape type.

The Visitor pattern is especially useful when you have many unrelated operations on an object structure and want to keep the code flexible and extensible.

Diagram

Loading graph...

Some other examples

Here are 10 real-world use cases for the Visitor design pattern:

  • Document Conversion: Visitor pattern can be used to convert a document from one format to another. Each element of the document can accept a visitor that applies a conversion operation.
  • Syntax Tree Processing: In compilers, the abstract syntax tree can be traversed using the Visitor pattern to generate machine code.
  • Rendering Graphical Objects: If you have a list of objects with different shapes (circle, square, etc.), you can use the Visitor pattern to render each object in a specific way.
  • Physics Simulation: In a physics engine, different objects can react differently to forces or collisions. The Visitor pattern can be used to apply these effects.
  • Operating System File Search: The Visitor pattern can be used to traverse file systems and perform operations like search, compression, calculation of storage space, etc.
  • Data Analysis: In data science, the Visitor pattern can be used to perform operations on structured data, like computing statistics, filtering data, visualizing data, etc.
  • Graph Algorithms: Visitor pattern can be used to implement algorithms that work on graphs, like depth-first search or breadth-first search.
  • Game Logic: In game development, the Visitor pattern can be used to implement game logic that depends on the types of two interacting game objects.
  • User Interface Layout: The Visitor pattern can be used to calculate layouts of user interface elements, where each type of UI element has a different way of calculating its size.
  • Object Persistence: The Visitor pattern can be used to persist objects to a database or other storage, where the persistence logic depends on the type of the object.

The Visitor pattern is useful when you have a complex structure (like a tree or graph), and you want to perform operations on that structure, but those operations depend on the types of the elements. It allows you to add new operations without changing the classes of the elements.


Recommended articles