Skip to main content

🧩 Visitor Pattern

✅ Intent

  • Separate operations (visitors) from the data structures they operate on
  • Add new behavior without modifying the data structure itself

✅ Motivation

  • Apply different operations externally to elements with multiple types or output formats
  • Cleanly separate output processing logic from the data structure

✅ When to Use

  • When the data structure is stable and only the logic changes or needs to be extended

✅ Code Example

// 要素インターフェース
interface Exportable {
accept(visitor: ExportVisitor): void;
}

// Visitor インターフェース
interface ExportVisitor {
visitPdf(pdf: PdfExporter): void;
visitCsv(csv: CsvExporter): void;
visitXml(xml: XmlExporter): void;
}

// 各要素(出力形式)
class PdfExporter implements Exportable {
constructor(private data: string) {}

export(): void {
console.log(`PDF出力: ${this.data}`);
}

accept(visitor: ExportVisitor): void {
visitor.visitPdf(this);
}
}

class CsvExporter implements Exportable {
constructor(private data: string) {}

export(): void {
console.log(`CSV出力: ${this.data}`);
}

accept(visitor: ExportVisitor): void {
visitor.visitCsv(this);
}
}

class XmlExporter implements Exportable {
constructor(private data: string) {}

export(): void {
console.log(`XML出力: ${this.data}`);
}

accept(visitor: ExportVisitor): void {
visitor.visitXml(this);
}
}

// Visitor 実装:ログ付き出力
class LoggingVisitor implements ExportVisitor {
visitPdf(pdf: PdfExporter): void {
console.log("開始ログ");
pdf.export();
console.log("完了ログ");
}

visitCsv(csv: CsvExporter): void {
console.log("開始ログ");
csv.export();
console.log("完了ログ");
}

visitXml(xml: XmlExporter): void {
console.log("開始ログ");
xml.export();
console.log("完了ログ");
}
}

// 利用例
const visitor = new LoggingVisitor();

const pdf = new PdfExporter("帳票データ");
pdf.accept(visitor);

const csv = new CsvExporter("ユーザー一覧");
csv.accept(visitor);

✅ Explanation

This code applies the Visitor pattern to add a common operation (logging-enabled export)
to various elements (PdfExporter, CsvExporter, XmlExporter) without modifying their structure.
The Visitor pattern is useful for defining new operations over an object structure without changing the classes of the elements.

1. Overview of the Visitor Pattern

  • Element: Interface that accepts a visitor

    • Represented by Exportable
  • ConcreteElement: Implements the Element and accepts the Visitor

    • Represented by PdfExporter, CsvExporter, and XmlExporter
  • Visitor: Interface that defines operations for each element type

    • Represented by ExportVisitor
  • ConcreteVisitor: Implements the visitor interface and provides specific logic

    • Represented by LoggingVisitor

2. Key Classes and Their Roles

  • Exportable

    • Common interface for exportable elements
    • Declares the method accept(visitor: ExportVisitor): void
  • PdfExporter, CsvExporter, XmlExporter

    • Concrete elements implementing Exportable
    • Export data in PDF, CSV, or XML formats
    • Implement accept to call the corresponding visitor method (visitPdf, visitCsv, visitXml)
  • ExportVisitor

    • Common interface for visitors
    • Declares methods for each element type (e.g., visitPdf, visitCsv, visitXml)
  • LoggingVisitor

    • A concrete visitor that adds logging to the export process for each type

3. UML Class Diagram

4. Benefits of the Visitor Pattern

  • Easy to Add New Operations: Add new operations without modifying the element classes
  • Separation of Concerns: Keeps element classes simple and delegates processing logic to visitors
  • Open/Closed Principle: New logic can be added without altering existing structures

This design is highly effective when shared operations need to be applied across a set of elements,
improving both extensibility and maintainability.