🧩 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
Element
and 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
accept
to 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.