Visitor Pattern
Separate algorithms from objects they operate on
Pattern Overview
👋 Visitor Pattern
The Visitor Pattern separates algorithms from the objects on which they operate. It lets you define new operations without changing the classes of elements on which they operate.
Core Concepts
🔹 Visitor Interface - Declares visit methods for each element type
🔹 Concrete Visitors - Implement specific operations on elements
🔹 Element Interface - Declares accept method that takes visitor
🔹 Double Dispatch - Method calls depend on both visitor and element types
Real-World Applications
Compilers - AST traversal for code generation, optimization, type checking File Systems - Operations on files/directories (size calculation, security scanning) Document Processing - Export to different formats (HTML, PDF, XML) Game Engines - Operations on game objects (rendering, collision, AI)Double Dispatch Magic
Single Dispatch - Method chosen based on receiver type only Double Dispatch - Method chosen based on both visitor and element types Result - Visitor pattern enables operation-specific behavior per element typeImplementation Benefits
✅ Open/Closed Principle - Easy to add new operations without modifying elements
✅ Single Responsibility - Operations grouped by visitor, not scattered across elements
✅ Centralized Operations - Related operations live in same visitor class
✅ Type Safety - Compile-time verification of visitor-element compatibility
Examples:
createShapeCollection()Shape operations executedcreateFileSystem()File system analyzedcreateAST()Code generated and evaluatedConcepts
Complexity Analysis
Implementation
shape-operations
// Visitor pattern for shape operations
interface Shape {
accept(visitor: ShapeVisitor): string;
}
interface ShapeVisitor {
visitCircle(circle: Circle): string;
visitRectangle(rectangle: Rectangle): string;
visitTriangle(triangle: Triangle): string;
}
class Circle implements Shape {
constructor(public radius: number) {}
accept(visitor: ShapeVisitor): string {
return visitor.visitCircle(this);
}
getArea(): number {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle implements Shape {
constructor(public width: number, public height: number) {}
accept(visitor: ShapeVisitor): string {
return visitor.visitRectangle(this);
}
getArea(): number {
return this.width * this.height;
}
}
class AreaCalculator implements ShapeVisitor {
visitCircle(circle: Circle): string {
const area = Math.PI * circle.radius * circle.radius;
return `Circle area: ${area.toFixed(2)}`;
}
visitRectangle(rectangle: Rectangle): string {
const area = rectangle.width * rectangle.height;
return `Rectangle area: ${area}`;
}
visitTriangle(triangle: Triangle): string {
const area = 0.5 * triangle.base * triangle.height;
return `Triangle area: ${area}`;
}
}