Visitor

Permite añadir nuevas operaciones a estructuras de objetos sin modificar sus clases.

Contexto

Tienes un AST estable de nodos (literal, suma, multiplicación) y quieres añadir operaciones (evaluar, imprimir, optimizar) sin tocar los nodos.

Problema

Cada nueva operación obliga a tocar todas las clases del AST.

Solución

Cada nodo expone Accept(visitor) y el visitor implementa un método por tipo de nodo.

#Ejemplo en C#

public interface IExprVisitor<T>
{
    T VisitLiteral(Literal n);
    T VisitAdd(Add n);
}

public abstract record Expr { public abstract T Accept<T>(IExprVisitor<T> v); }
public record Literal(int Value) : Expr { public override T Accept<T>(IExprVisitor<T> v) => v.VisitLiteral(this); }
public record Add(Expr L, Expr R) : Expr { public override T Accept<T>(IExprVisitor<T> v) => v.VisitAdd(this); }

public class Evaluator : IExprVisitor<int>
{
    public int VisitLiteral(Literal n) => n.Value;
    public int VisitAdd(Add n)         => n.L.Accept(this) + n.R.Accept(this);
}

En C# moderno, pattern matching sobre records suele ser más simple que un Visitor completo.

Cuándo NO aplicarlo
  • Cuando la jerarquía cambia mucho (cada cambio rompe TODOS los visitors).
  • Cuando switch expression con pattern matching ya cubre la operación.
Tradeoffs
Pro Contra
Añadir operaciones sin tocar nodos Añadir nodos rompe todos los visitors

#behavioral #gof #double-dispatch