🧩 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
- TypeScript
- PHP
- Python
// 要素インターフェース
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);
<?php
interface ExportVisitor {
public function visitPdf(PdfExporter $pdf): void;
public function visitCsv(CsvExporter $csv): void;
public function visitXml(XmlExporter $xml): void;
}
interface Exportable {
public function accept(ExportVisitor $visitor): void;
}
class PdfExporter implements Exportable {
public function __construct(private string $data) {}
public function export(): void {
echo "PDF出力: {$this->data}\n";
}
public function accept(ExportVisitor $visitor): void {
$visitor->visitPdf($this);
}
}
class CsvExporter implements Exportable {
public function __construct(private string $data) {}
public function export(): void {
echo "CSV出力: {$this->data}\n";
}
public function accept(ExportVisitor $visitor): void {
$visitor->visitCsv($this);
}
}
class XmlExporter implements Exportable {
public function __construct(private string $data) {}
public function export(): void {
echo "XML出力: {$this->data}\n";
}
public function accept(ExportVisitor $visitor): void {
$visitor->visitXml($this);
}
}
class LoggingVisitor implements ExportVisitor {
public function visitPdf(PdfExporter $pdf): void {
echo "開始ログ\n";
$pdf->export();
echo "完了ログ\n";
}
public function visitCsv(CsvExporter $csv): void {
echo "開始ログ\n";
$csv->export();
echo "完了ログ\n";
}
public function visitXml(XmlExporter $xml): void {
echo "開始ログ\n";
$xml->export();
echo "完了ログ\n";
}
}
// 利用例
$visitor = new LoggingVisitor();
$pdf = new PdfExporter("帳票データ");
$pdf->accept($visitor);
$csv = new CsvExporter("ユーザー一覧");
$csv->accept($visitor);
from abc import ABC, abstractmethod
# Visitor インターフェース
class ExportVisitor(ABC):
@abstractmethod
def visit_pdf(self, pdf): pass
@abstractmethod
def visit_csv(self, csv): pass
@abstractmethod
def visit_xml(self, xml): pass
# Exportable インターフェース
class Exportable(ABC):
@abstractmethod
def accept(self, visitor: ExportVisitor): pass
# 出力形式
class PdfExporter(Exportable):
def __init__(self, data: str):
self.data = data
def export(self):
print(f"PDF出力: {self.data}")
def accept(self, visitor: ExportVisitor):
visitor.visit_pdf(self)
class CsvExporter(Exportable):
def __init__(self, data: str):
self.data = data
def export(self):
print(f"CSV出力: {self.data}")
def accept(self, visitor: ExportVisitor):
visitor.visit_csv(self)
class XmlExporter(Exportable):
def __init__(self, data: str):
self.data = data
def export(self):
print(f"XML出力: {self.data}")
def accept(self, visitor: ExportVisitor):
visitor.visit_xml(self)
# Visitor 実装
class LoggingVisitor(ExportVisitor):
def visit_pdf(self, pdf: PdfExporter):
print("開始ログ")
pdf.export()
print("完了ログ")
def visit_csv(self, csv: CsvExporter):
print("開始ログ")
csv.export()
print("完了ログ")
def visit_xml(self, xml: XmlExporter):
print("開始ログ")
xml.export()
print("完了ログ")
# 利用例
visitor = LoggingVisitor()
pdf = PdfExporter("帳票データ")
pdf.accept(visitor)
csv = 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
- Represented by
-
ConcreteElement: Implements the
Elementand accepts theVisitor- Represented by
PdfExporter,CsvExporter, andXmlExporter
- Represented by
-
Visitor: Interface that defines operations for each element type
- Represented by
ExportVisitor
- Represented by
-
ConcreteVisitor: Implements the visitor interface and provides specific logic
- Represented by
LoggingVisitor
- Represented by
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
acceptto call the corresponding visitor method (visitPdf,visitCsv,visitXml)
- Concrete elements implementing
-
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.