Pythonで実装するVisitorパターン：要素構造と操作ロジックを分離する



## 概要

**Visitor（ビジター）パターン**は、  
データ構造を変更せずに、**さまざまな操作（処理）を要素に対して適用できるようにする設計パターン**である。

要素構造（ノードやオブジェクト）と処理ロジック（操作）を分離することで、**新しい処理を追加しやすく、構造に手を加えずに振る舞いを拡張できる**。

---

## 1. なぜVisitorが必要か？

### ❌ 処理を要素内部に詰め込むと、機能追加のたびに構造をいじる羽目になる

```python
class Node:
    def render(self): ...
    def export(self): ...
    def validate(self): ...
```

→ **単一責任が崩れ、要素側が過剰に肥大化する**

---

### ✅ Visitorに処理を分離し、要素はそれを受け入れるだけの構造にする

```python
node.accept(visitor)
```

→ **処理ロジックは外部化され、構造に手を加えずに拡張が可能**

---

## 2. 基本構造

### ✅ Visitorインターフェース

```python
class Visitor:
    def visit_file(self, element):
        raise NotImplementedError

    def visit_folder(self, element):
        raise NotImplementedError
```

---

### ✅ Element（訪問される側）

```python
class Element:
    def accept(self, visitor: Visitor):
        raise NotImplementedError
```

---

### ✅ ConcreteElement（構造を持つオブジェクト）

```python
class File(Element):
    def __init__(self, name):
        self.name = name

    def accept(self, visitor):
        visitor.visit_file(self)

class Folder(Element):
    def __init__(self, name, children=None):
        self.name = name
        self.children = children or []

    def accept(self, visitor):
        visitor.visit_folder(self)
```

---

### ✅ ConcreteVisitor（具体的な操作）

```python
class ExportVisitor(Visitor):
    def visit_file(self, element):
        print(f"[Exporting file] {element.name}")

    def visit_folder(self, element):
        print(f"[Exporting folder] {element.name}")
        for child in element.children:
            child.accept(self)
```

---

### ✅ 使用例

```python
root = Folder("root", [
    File("a.txt"),
    File("b.txt"),
    Folder("sub", [File("c.txt")])
])

visitor = ExportVisitor()
root.accept(visitor)
```

出力:
```
[Exporting folder] root  
[Exporting file] a.txt  
[Exporting file] b.txt  
[Exporting folder] sub  
[Exporting file] c.txt
```

---

## 3. Python的応用：`singledispatchmethod` による簡易Visitor

```python
from functools import singledispatchmethod

class Visitor:
    @singledispatchmethod
    def visit(self, arg):
        raise NotImplementedError

    @visit.register
    def _(self, arg: File):
        print(f"[ファイル処理] {arg.name}")

    @visit.register
    def _(self, arg: Folder):
        print(f"[フォルダ処理] {arg.name}")
        for c in arg.children:
            self.visit(c)
```

→ **型ベースで処理を分岐させ、構文的Visitorパターンを構成**

---

## 4. 実務ユースケース

### ✅ AST（抽象構文木）に対する変換・解析

→ **PythonのAST、SQL構文、Markdownの解析などに応用**

---

### ✅ ファイルシステムの操作（検証・エクスポート・変換）

→ ディレクトリ構造に対して、**目的ごとのVisitorを適用**

---

### ✅ UIツリーへの処理適用（検証、描画、トラバース）

→ **DOM操作やカスタム描画処理の分離**

---

### ✅ DSL（ドメイン言語）のインタプリタ・トランスパイラ構築

→ 各ノード型に対して**意味論的処理を定義**

---

## 5. よくある誤用と対策

### ❌ VisitorがElementに過剰に依存して型結合する

→ ✅ **Visitorの設計は拡張に留め、Element側を柔らかく保つ**

---

### ❌ 処理の追加は簡単だが、要素構造の追加が難しくなる

→ ✅ **要素の拡張が必要な場合はBuilderやCompositeと併用**

---

### ❌ accept() が単なる `visitor.visit(self)` の機械的な呼び出しに留まる

→ ✅ **自己参照や再帰構造を活用し、ツリー構造に合わせて設計する**

---

## 結語

**Visitorパターンとは、“構造と処理を分離し、異なるロジックを外部から柔軟に注入する設計”である。**

- 構造はそのままに、**処理を増やすことが容易**  
- AST・ファイル・UI・ドキュメントなど、**ツリー構造を対象にした汎用的パターン**  
- Pythonでは `accept()` メソッドと `@singledispatchmethod` により、**シンプルかつ強力に表現可能**

**Pythonicとは、“構造に触れず、振る舞いだけを変えること”。  
Visitorパターンはその処理の知性を、設計の柔軟性として昇華する技法である。**