Type Behavioral
Purpose Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Description

The Visitor pattern separates algorithms from the objects on which they operate. It is particularly useful when working with an object structure whose elements may differ in type, but where the same new operation needs to be applied across all of them.

In effect, Visitor makes it possible to add new operations to an existing object structure without changing the classes to include those operations directly. Instead, the behaviour is placed in separate visitor classes, each of which implements a particular operation across the set of element types.

There is, however, one important structural requirement: the element classes must provide an Accept operation so that they can be visited. This method allows the visitor to be applied to the element and is the mechanism that enables the pattern's double-dispatch behaviour.

Once Accept is in place, new operations can be introduced by adding new concrete visitors, without further modifying the element classes themselves. The Accept operation provides the dispatch mechanism that allows those external operations to vary according to the concrete element type.

Visitor works particularly well when the set of element types is relatively stable but new operations need to be introduced frequently. The trade-off is that adding a new element type generally requires changing the Visitor interface and updating each existing concrete visitor.

Visitor adds operations to an object structure without changing the element classes each time.


Overview

Visitor
Visitor An interface which declares a set of visiting methods that correspond to classes of the concrete elements. These methods may have identical names if the program is written in a language that supports overloading.
ConcreteVisitor Implements several versions of the same behaviors, tailored for different concrete element classes.
Element An Interface that declares a method for accepting visitors.
This method should have one parameter declared with the type of the visitor interface.
ObjectStructure Contains or provides access to the elements over which visitors operate.
ConcreteElements Classes which implement the accepting method. The purpose of this method is to redirect the call to the proper visitor's method corresponding to the current element class. Note that even if a base element class has this method, all subclasses must still implement this method in their own classes and call the appropriate method on the visitor object.
Client Creates or selects a visitor and applies it to elements in the object structure.

Implementation

// Element

public interface IAnimal
{
    void Accept(IAnimalOperation operation);
}

// Concrete Elements

public class Monkey : IAnimal
{
    public void Accept(IAnimalOperation operation)
        => operation.VisitMonkey(this);
}

public class Lion : IAnimal
{
    public void Accept(IAnimalOperation operation)
        => operation.VisitLion(this);
}

public class Dolphin : IAnimal
{
    public void Accept(IAnimalOperation operation)
        => operation.VisitDolphin(this);
}

// Visitor

public interface IAnimalOperation
{
    void VisitMonkey(Monkey monkey);
    void VisitLion(Lion lion);
    void VisitDolphin(Dolphin dolphin);
}

// ConcreteVisitors

public class Speak : IAnimalOperation
{
    public void VisitDolphin(Dolphin dolphin)
        => Console.WriteLine("Dolphin makes sound");
    
    public void VisitLion(Lion lion)
        => Console.WriteLine("Lion roars");

    public void VisitMonkey(Monkey monkey)
        => Console.WriteLine("Monkey screams");
}

public class Feed : IAnimalOperation
{
    public void VisitMonkey(Monkey monkey)
        => Console.WriteLine("Feeding the monkey with bananas...");

    public void VisitLion(Lion lion)
        => Console.WriteLine("Feeding the lion with meat...");

    public void VisitDolphin(Dolphin dolphin)
        => Console.WriteLine("Feeding the dolphin with fish...");
}

// Client

public static class VisitorDemo
{
    public static void Execute()
    {
        List<IAnimal> animals = [new Monkey(), new Lion(), new Dolphin()];
        List<IAnimalOperation> operations = [new Speak(), new Feed()];

        foreach (var operation in operations)
        {
            foreach (var animal in animals)
            {
                animal.Accept(operation);
            }
        }
    }
}

Summary

For more information, please see here.