概要
Visitor(ビジター)パターンは、
オブジェクト構造に対する操作を、構造クラスから分離して**「訪問者」として外部に定義する**ことで、
構造を変えずに新しい操作(振る舞い)を追加可能にするパターンである。
複雑な構文木やオブジェクトの階層構造に対し、
共通インターフェースを介して柔軟に処理を切り替えることが可能になる。
1. なぜVisitorが必要か?
❌ 構造オブジェクト自身が処理ロジックを持ちすぎて肥大化
class Node:
def evaluate(self): ...
def print_tree(self): ...
def optimize(): ...
→ 各処理が構造の中に密結合し、拡張のたびに修正が必要
✅ Visitorを導入し、処理ロジックを外部クラスに分離
node.accept(Evaluator())
node.accept(Printer())
→ 構造をそのままに、処理を訪問者クラスとして外部化・追加可能に
2. 基本構造
✅ Element(構造側の抽象)
class Node:
def accept(self, visitor):
raise NotImplementedError
✅ ConcreteElements(構造定義)
class NumberNode(Node):
def __init__(self, value):
self.value = value
def accept(self, visitor):
return visitor.visit_number(self)
class AddNode(Node):
def __init__(self, left: Node, right: Node):
self.left = left
self.right = right
def accept(self, visitor):
return visitor.visit_add(self)
✅ Visitor(処理の抽象)
class Visitor:
def visit_number(self, node): pass
def visit_add(self, node): pass
✅ ConcreteVisitor(処理定義)
class Evaluator(Visitor):
def visit_number(self, node):
return node.value
def visit_add(self, node):
return node.left.accept(self) + node.right.accept(self)
✅ 使用例
tree = AddNode(NumberNode(3), AddNode(NumberNode(2), NumberNode(1)))
evaluator = Evaluator()
result = tree.accept(evaluator)
print(result) # 6
3. Python的応用:@singledispatchmethod
を使った動的ディスパッチ
from functools import singledispatchmethod
class DynamicVisitor:
@singledispatchmethod
def visit(self, node):
raise NotImplementedError
@visit.register
def _(self, node: NumberNode):
return node.value
@visit.register
def _(self, node: AddNode):
return self.visit(node.left) + self.visit(node.right)
→ マルチメソッド的な構造により、Pythonらしく実装可能
4. 実務ユースケース
✅ 抽象構文木の評価・最適化
→ パーサ/インタプリタにおける式の評価・表示・変換の切り替え
✅ ファイルシステム構造の走査とレポート生成
→ ファイル・ディレクトリに対してサイズ集計/表示/圧縮などを分離実装
✅ UIコンポーネント構造へのイベント処理適用
→ ボタン/入力欄/パネルなどの描画/検証/保存処理をVisitor化
✅ ビジネスルールの外部定義・適用
→ 各ドメインオブジェクトに対する検証・計算・集計処理を注入
5. よくある誤用と対策
❌ Visitorクラスが全ての構造型を知ってしまう
→ ✅ Visitorの拡張は必要に応じて分割し、単一責務を守る
❌ Element側のacceptに処理が入りすぎる
→ ✅ accept
はVisitorの呼び出しに限定すべき
❌ 構造が頻繁に変更されるプロジェクトに無理に適用
→ ✅ Visitorは構造が安定していて、処理が頻繁に追加される場面に適す
結語
Visitorパターンとは、“構造に手を加えず、処理だけを拡張するための訪問設計”である。
- 処理の拡張をクラス外に独立させ、構造との疎結合を実現
- 複数の処理(表示/最適化/検証/計算など)を、構造の再利用性を保ったまま外部化
- Pythonではダックタイピングやマルチディスパッチを活用し、柔軟かつ簡潔にVisitor設計を実現できる
Pythonicとは、“構造と責務を分離して洗練させる”ことであり、
Visitorパターンはその責務の訪問者として、コードの中に拡張性を宿す技法である。