100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

How Does the Visitor Pattern Use Double Dispatch?

How the Visitor design pattern uses double dispatch via accept()/visit() to add operations without modifying element classes.

hardQ193 of 226 in Object Oriented Programming Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

The Visitor pattern lets you add new operations over a fixed class hierarchy without modifying the hierarchy itself, by defining an accept(Visitor) method on each element that calls back a matching visit() overload on the visitor, which is exactly the double-dispatch mechanism.

Each element class implements accept(Visitor v) as { v.visit(this); }. Because accept() is virtual, calling shape.accept(visitor) resolves to the concrete element’s accept() at runtime (first dispatch). Inside that method, "this" has a known concrete compile-time type (e.g. Circle), so v.visit(this) picks the matching visit(Circle) overload on the visitor at compile time, but which visit() actually runs is still routed through the visitor’s own virtual dispatch too (second dispatch). The net effect is that the correct operation runs based on both the element’s and the visitor’s concrete types, without a single instanceof check. The tradeoff is the classic Visitor limitation: adding a new element type requires updating every existing Visitor implementation, since the visitor interface must add a new visit() overload.

  • Adds new operations without modifying existing element classes
  • Eliminates long instanceof/switch chains for type-pair logic
  • Groups related operations together in one Visitor class
  • Type-safe: the compiler enforces every element type is handled

AI Mentor Explanation

A tour report writer visits each match type (Test, ODI, T20) and produces a different summary style for each, without the match classes themselves knowing anything about report writing. Each match’s “acceptReport(writer)” method calls back “writer.write(this)”, and the writer’s own overloaded write() methods handle Test matches differently from T20s. New report styles (a highlights writer, a stats writer) can be added later without touching the match classes at all, which is the Visitor pattern using double dispatch.

Step-by-Step Explanation

  1. Step 1

    Define accept() on every element

    Each element class in the hierarchy implements accept(Visitor v) { v.visit(this); }.

  2. Step 2

    Define overloaded visit() on the visitor

    The Visitor interface declares one visit() overload per concrete element type.

  3. Step 3

    First dispatch on the element

    Calling element.accept(visitor) resolves virtually to the element’s concrete accept() implementation.

  4. Step 4

    Second dispatch on the visitor

    Inside accept(), v.visit(this) resolves to the matching overload because “this” has the element’s known concrete type.

What Interviewer Expects

  • Clear walk-through of accept()/visit() as the two dispatch steps
  • Explanation of why a single virtual call alone cannot pick the right operation
  • Awareness of the tradeoff: adding a new element type breaks all existing Visitors
  • A code example showing the accept/visit pairing

Common Mistakes

  • Describing Visitor only as “a way to add operations” without explaining the dispatch mechanism
  • Forgetting that adding a new element type requires updating every Visitor implementation
  • Confusing accept() with visit() or reversing their roles
  • Not mentioning that visit() is overloaded per concrete element type

Best Answer (HR Friendly)

The Visitor pattern lets you add new operations to a group of related classes without changing those classes. It works by having each class call back into a visitor object, and that callback resolves to the right method based on the class’s actual type. That two-step handoff, first picking the right accept method and then the right visit method, is what double dispatch means in practice.

Code Example

Visitor pattern implementing double dispatch
interface Element {
    void accept(Visitor v);
}

class Book implements Element {
    double price = 20;
    public void accept(Visitor v) { v.visit(this); }
}

class Electronics implements Element {
    double price = 100;
    public void accept(Visitor v) { v.visit(this); }
}

interface Visitor {
    void visit(Book b);
    void visit(Electronics e);
}

class TaxVisitor implements Visitor {
    public void visit(Book b) { System.out.println("Book tax: 0%"); }
    public void visit(Electronics e) { System.out.println("Electronics tax: 18%"); }
}

Element[] cart = { new Book(), new Electronics() };
Visitor tax = new TaxVisitor();
for (Element e : cart) e.accept(tax); // double dispatch per item

Follow-up Questions

  • What happens to existing Visitor implementations when a new element type is added?
  • Why can’t you achieve the same result with a single overloaded method?
  • How does the Visitor pattern relate to the Open/Closed Principle?
  • When would you avoid the Visitor pattern in favor of simple polymorphism?

MCQ Practice

1. In the Visitor pattern, what does accept(Visitor v) do?

accept() calls back into the visitor with v.visit(this), which is the second half of the double-dispatch chain.

2. A known drawback of the Visitor pattern is?

Because the Visitor interface must declare a visit() overload per element type, new element types force changes across all visitors.

3. Why is v.visit(this) able to pick the correct overload inside accept()?

Inside a concrete element’s own accept() method, "this" is statically typed as that concrete class, so overload resolution picks the matching visit().

Flash Cards

What two methods implement double dispatch in Visitor?accept() on the element and the overloaded visit() on the visitor.

Main benefit of Visitor?Add new operations over a hierarchy without modifying the hierarchy’s classes.

Main drawback of Visitor?Adding a new element type requires updating every existing Visitor implementation.

Which principle does Visitor support for operations?Open/Closed Principle — open for new operations, closed for element modification.

1 / 4

Continue Learning