Visitor memisahkan algoritma dari struktur data yang diproses. Kamu bisa menambah operasi baru tanpa mengubah class struktur data.
Kapan berguna:
- Struktur data stabil (jarang berubah), tapi operasi yang diterapkan sering bertambah
- Operasi punya logika kompleks yang tidak cocok di-inline di class data
- Kamu ingin memisahkan concerns: satu class = satu operasi
Contoh klasik: AST (Abstract Syntax Tree) traversal
Parser menghasilkan tree node: NumberLiteral, Variable, BinaryOp. Kita ingin beberapa operasi: evaluate, print, optimize, typecheck.
Tanpa Visitor:
class NumberLiteral {
constructor(value) { this.value = value; }
evaluate() { return this.value; }
print() { return String(this.value); }
optimize() { return this; }
typecheck() { return "number"; }
// Tiap operasi baru → edit SEMUA class node
}
Dengan Visitor:
class NumberLiteral {
constructor(value) { this.value = value; }
accept(visitor) { return visitor.visitNumber(this); }
}
class BinaryOp {
constructor(op, left, right) {
this.op = op;
this.left = left;
this.right = right;
}
accept(visitor) { return visitor.visitBinaryOp(this); }
}
// Operasi jadi class terpisah
class Evaluator {
visitNumber(node) { return node.value; }
visitBinaryOp(node) {
const l = node.left.accept(this);
const r = node.right.accept(this);
return node.op === "+" ? l + r : l - r;
}
}
class Printer {
visitNumber(node) { return String(node.value); }
visitBinaryOp(node) {
return `(${node.left.accept(this)} ${node.op} ${node.right.accept(this)})`;
}
}
// Penggunaan:
const tree = new BinaryOp("+", new NumberLiteral(1), new NumberLiteral(2));
tree.accept(new Evaluator()); // 3
tree.accept(new Printer()); // "(1 + 2)"
Double dispatch — inti trick:
tree.accept(visitor)memanggil method spesifik visitor berdasarkan tipetree- Dengan polymorphism biasa, method dispatch hanya berdasarkan
this(satu tipe) - Visitor simulasikan dispatch 2-dimensi: (tipe node × tipe operasi)
Kelebihan:
- Menambah operasi = tambah class Visitor baru (tidak sentuh node classes)
- Logika satu operasi tersentralisasi di satu class
- Bisa share state antar node dalam satu traversal (simpan di visitor instance)
Kekurangan:
- Menambah TIPE NODE baru = update semua Visitor (OCP flip — trade-off)
- Lebih banyak boilerplate
- Sirkular dependency: Visitor perlu tahu semua tipe node
JavaScript/TypeScript alternative (tidak perlu strict Visitor):
// Tipe tag-based dispatch via switch (lebih idiomatic di TS)
type Node =
| { type: "number"; value: number }
| { type: "binary"; op: string; left: Node; right: Node };
const evaluate = (n: Node): number => {
switch (n.type) {
case "number": return n.value;
case "binary":
const l = evaluate(n.left);
const r = evaluate(n.right);
return n.op === "+" ? l + r : l - r;
}
};
TypeScript exhaustiveness check memastikan semua case ter-handle. Ini adalah functional equivalent dari Visitor — sama benefit dengan lebih sedikit ceremony.
Kapan pakai Visitor murni vs tag dispatch:
- Visitor class-based — OOP kode yang strict, banyak state dalam traversal
- Tag dispatch — TS/FP style, simple operation, compile-time exhaustive check