Visitor
Overview
Add operations to object structures without changing their classes.
When to use
- You need to run multiple operations on a stable object structure.
- Adding new operations is more common than adding new node types.
Java example
interface Visitor {
void visit(TextNode node);
void visit(ImageNode node);
}
interface DocNode {
void accept(Visitor visitor);
}
class TextNode implements DocNode {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class ImageNode implements DocNode {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
TypeScript example
interface Visitor {
visitText(node: TextNode): void;
visitImage(node: ImageNode): void;
}
interface DocNode {
accept(visitor: Visitor): void;
}
class TextNode implements DocNode {
accept(visitor: Visitor): void {
visitor.visitText(this);
}
}
class ImageNode implements DocNode {
accept(visitor: Visitor): void {
visitor.visitImage(this);
}
}
Pros and cons
Pros:
- New operations can be added without modifying nodes.
- Separates algorithms from object structure.
Cons:
- Adding new node types requires updating all visitors.
- Can be verbose.
Common pitfalls
- Using Visitor for small object graphs.
- Forgetting to update all visitors after new nodes.