Skip to main content

Command Palette

Search for a command to run...

Week 22 — Behavioral Design Pattern: Visitor Pattern

Design Patterns Demystified Series

Updated
6 min read
Week 22 — Behavioral Design Pattern: Visitor Pattern

Design Patterns Demystified Series

This week, we’re exploring the Visitor Pattern, a behavioral design pattern that allows you to add new operations to an existing object structure without modifying the objects themselves.

It’s commonly used in compilers, document processing systems, financial reporting tools, and inspection workflows.

Let’s understand it with a simple museum analogy and clean Java code.


🧠 What is the Visitor Pattern?

The Visitor Pattern lets you define new operations on objects without changing their classes.

Instead of placing all behaviors inside the objects themselves, we move those behaviors into separate visitor classes.

The objects simply accept visitors, and the visitor performs its operation.

In simple terms:

Stable objects → Different operations applied → Operations live outside the objects


🏛 Real-World Analogy: Museum Exhibits and Visitors

Imagine a museum with different exhibits:

  • Paintings

  • Sculptures

  • Historical artifacts

Now imagine different professionals visiting the museum:

  1. Tour Guide → Explains history and background

  2. Maintenance Staff → Checks preservation and damage

The exhibits themselves do not change, but different visitors perform different operations on them.

For example:

Tour Guide

  • Explains the painting’s history

  • Describes the sculptor and era

  • Explains the artifact’s origin

Maintenance Staff

  • Checks the painting for damage

  • Inspects sculptures for cracks

  • Verifies artifact preservation

The exhibits stay the same, but the actions performed on them vary depending on the visitor.

That’s exactly what the Visitor Pattern models in software.


💻 Let’s Translate That Into Code

Step 1: Visitor Interface

The visitor defines operations that can be performed on each type of exhibit.

interface MuseumVisitor {
  void visit(Painting painting);
  void visit(Sculpture sculpture);
  void visit(Artifact artifact);
}

🔎 What’s happening here?

The visitor interface declares a visit method for every element type.

Different exhibits may require different logic, so each type has its own visit method.

Step 2: Exhibit Interface (Element)

Each exhibit must allow a visitor to perform operations on it.

interface Exhibit {
  void accept(MuseumVisitor visitor);
}

The accept() method allows a visitor to visit the exhibit and execute its logic.

Step 3: Concrete Exhibits

These represent the objects being visited.

Painting

class Painting implements Exhibit {
  @Override
    public void accept(MuseumVisitor visitor) {
        visitor.visit(this);
    }
}

Sculpture

class Sculpture implements Exhibit {
  @Override
    public void accept(MuseumVisitor visitor) {
        visitor.visit(this);
    }
}

Artifact

class Artifact implements Exhibit {
  @Override
    public void accept(MuseumVisitor visitor) {
        visitor.visit(this);
    }
}

Each exhibit simply accepts the visitor and passes itself.

This allows the visitor to decide what operation should be performed.

Step 4: Tour Guide Visitor

class TourGuideVisitor implements MuseumVisitor {
  @Override
    public void visit(Painting painting) {
        System.out.println("Tour Guide: Explaining the history of the painting");
    }
    @Override
    public void visit(Sculpture sculpture) {
        System.out.println("Tour Guide: Explaining the sculpture's artist and era");
    }
    @Override
    public void visit(Artifact artifact) {
        System.out.println("Tour Guide: Explaining the artifact's origin");
    }
}

This visitor performs educational operations on exhibits.

Step 5: Maintenance Visitor

class MaintenanceVisitor implements MuseumVisitor {
  @Override
    public void visit(Painting painting) {
        System.out.println("Maintenance: Checking painting for damage");
    }
    @Override
    public void visit(Sculpture sculpture) {
        System.out.println("Maintenance: Inspecting sculpture for cracks");
    }
    @Override
    public void visit(Artifact artifact) {
        System.out.println("Maintenance: Verifying artifact preservation");
    }
}

This visitor performs inspection and maintenance operations.

Step 6: Client Code

public class VisitorPatternDemo {
  public static void main(String[] args) {
        Exhibit[] exhibits = {
            new Painting(),
            new Sculpture(),
            new Artifact()
        };
        MuseumVisitor tourGuide = new TourGuideVisitor();
        MuseumVisitor maintenance = new MaintenanceVisitor();
        System.out.println("=== Tour Guide Visit ===");
        for (Exhibit exhibit : exhibits) {
            exhibit.accept(tourGuide);
        }
        System.out.println("\n=== Maintenance Inspection ===");
        for (Exhibit exhibit : exhibits) {
            exhibit.accept(maintenance);
        }
    }
}

✅ Output

=== Tour Guide Visit ===
Tour Guide: Explaining the history of the painting
Tour Guide: Explaining the sculpture's artist and era
Tour Guide: Explaining the artifact's origin
=== Maintenance Inspection ===
Maintenance: Checking painting for damage
Maintenance: Inspecting sculpture for cracks
Maintenance: Verifying artifact preservation

🎯 What This Demonstrates

This example highlights the key ideas behind the Visitor Pattern.

The exhibits stay unchanged, but different visitors perform different operations.

So we get:

  • Separation of data and behavior

  • Ability to add new operations easily

  • Cleaner and more organized code

The museum exhibits are stable, but new visitors can be added anytime.


🧩 Why Use the Visitor Pattern?

Advantages

Adds new operations easily

You can introduce new behavior without modifying existing classes.

Separates behavior from objects

Objects remain simple and focused on their data.

Follows the Open/Closed Principle

Classes are open for extension but closed for modification.

Centralizes related logic

All logic related to an operation is grouped inside a visitor.


When to Use It

Use the Visitor Pattern when:

  • You have a stable object structure

  • Many unrelated operations need to be performed

  • You want to avoid modifying existing classes

  • You want to separate operations from data structures

🌍 Real-World Use Cases

Visitor Pattern is widely used in real systems such as:

Compilers

Visitors traverse Abstract Syntax Trees (AST) to perform tasks like code generation and optimization.

Document processing systems

Visitors handle operations like:

  • exporting

  • formatting

  • spell checking

  • analytics

Financial systems

Visitors perform:

  • tax calculations

  • reporting

  • auditing

Game engines

Game objects may be visited by:

  • rendering systems

  • physics engines

  • collision detection systems

🔜 Next Up: Interpreter pattern

Next week, we’ll explore the Interpreter Pattern, a behavioral design pattern used to define and evaluate the grammar of a language.

Stay tuned as we break it down with beginner-friendly code examples, relatable analogies, and real-world use cases. We’ll help you write cleaner, more flexible, and more maintainable code — one pattern at a time.


✅ Call to Action (CTA)

🚀 Enjoying the Design Patterns Demystified Series?

Follow the series as we break down each pattern with simple analogies, clean code examples, and real-world use cases to help you write better and more maintainable software.

Next week: Design Pattern Recap — Choosing the Right Pattern for the Right Problem.


Note: AI-generated Image

Design Patterns

Part 23 of 25

Let’s be honest—most devs would rather code than study design patterns. They seem academic and full of jargon. But this guide is different: plain language, real-life analogies, and practical examples that make your code smarter, cleaner, and scalable

Up next

Week 23 - Behavioral Design Pattern: Interpreter Pattern

Design Patterns Demystified Series