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 expressioncon 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