Design Patterns Mastery

Visitor Pattern: Separating operations from data structures

1 Views Updated 5/4/2026

The Visitor Pattern

The Visitor Pattern is used to separate 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 the structures themselves.

1. Scenario: Document Export

You have a Document class containing Paragraph and Image nodes. Now you want to add Export to PDF and Export to HTML. Instead of putting ToPdf() methods inside Paragraph and Image classes, you create a Visitor.

public interface IVisitor 
{
    void Visit(Paragraph p);
    void Visit(Image i);
}

public class PdfVisitor : IVisitor { ... }

2. Double Dispatch

The Visitor pattern uses a technique called Double Dispatch: the element calls visitor.Visit(this). This allows the visitor to know the *exact runtime type* of the object it is dealing with, even when processing a generic list of base components.

4. Interview Mastery

Q: "Why is the Visitor pattern often criticized as an anti-pattern?"

Architect Answer: "Because it is **Inflexible for Data Changes**. If you add a new element type (e.g., `Table`), you MUST update the `IVisitor` interface and EVERY single concrete visitor class in the system. It breaks the Open/Closed Principle for data. You should ONLY use Visitor when your object structure is **Fixed** (rarely changes) but you expect to add many new **Operations** over time."

Design Patterns Mastery
1. Introduction to Design Patterns
Introduction to GoF Patterns: Why patterns matter? SOLID Principles: The foundation of all patterns DRY, KISS, and YAGNI (Architectural Philosophy)
2. Creational Patterns
Singleton Pattern: Thread-safety & Captive Dependencies Factory Method: Abstracting complex object creation Abstract Factory: Creating families of related objects Builder Pattern: Constructing complex fluents Prototype Pattern: Cloning high-cost objects
3. Structural Patterns
Adapter Pattern: Bridging incompatible interfaces Bridge Pattern: Decoupling abstraction from implementation Composite Pattern: Managing tree structures & hierarchies Decorator Pattern: Enhancing behavior without inheritance Facade Pattern: Simplifying complex library subsystems Flyweight Pattern: Drastically reducing memory footprint Proxy Pattern: Interception, Lazy-Loading, and Security
4. Behavioral Patterns
Chain of Responsibility: Middleware & Pipeline Architecture Command Pattern: Implementing Undo/Redo & Queueing Interpreter Pattern: Building domain-specific languages Iterator Pattern: Unified traversal of collections Mediator Pattern: Decoupling components with MediatR Memento Pattern: Capturing and restoring object state Observer Pattern: Pub/Sub & Event-driven architecture State Pattern: Managing complex object lifecycles Strategy Pattern: Swappable algorithms at runtime Template Method: Defining skeleton algorithms Visitor Pattern: Separating operations from data structures
5. Modern Enterprise & Cloud Patterns
Repository & Unit of Work (The EF Core Standard) CQRS Pattern (Command Query Responsibility Segregation) Circuit Breaker & Retry Patterns (Resilience with Polly) Dependency Injection Pattern (The Modern King)