Что такое Visitor и double dispatch?
Поведенческие паттерны
Visitor выносит операции над иерархией классов в отдельный объект, а нужный метод выбирается двумя вызовами (double dispatch): сначала по типу элемента, затем по типу посетителя.
Зачем
- Иерархия узлов стабильна (AST:
Num,Add), а операций много: вычисление, печать, оптимизация, проверка типов. Новая операция — новый посетитель, классы узлов не трогаем. - Обратная сторона — expression problem: новый тип узла требует правки всех посетителей.
Double dispatch
Java, C# и JS/TS выбирают метод по динамическому типу только получателя (single dispatch); перегрузки visit(Add)/visit(Num) разрешаются статически, а в JS/TS их в рантайме нет вовсе. Visitor обходит это: node.accept(v) — выбор по узлу, внутри v.visitAdd(this) — по посетителю.
// Посетитель — объект с методами visitNum(n) и visitAdd(n)
class Num {
constructor(value) { this.value = value; }
accept(v) { return v.visitNum(this); }
}
class Add {
constructor(left, right) { this.left = left; this.right = right; }
accept(v) { return v.visitAdd(this); }
}
const evaluate = {
visitNum: (n) => n.value,
visitAdd: (n) => n.left.accept(evaluate) + n.right.accept(evaluate),
};
console.log(new Add(new Num(2), new Num(3)).accept(evaluate)); // 5Коротко для собеседования
Visitor собирает операции над стабильной иерархией, например над узлами AST, в отдельных объектах, а связку accept → visitX использует как ручной double dispatch. Добавлять операции легко, а новый тип узла требует правки всех посетителей; в JS/TS часто проще switch по полю kind.