概要
Visitorパターンは、
オブジェクトの構造に変更を加えずに、外部から新たな振る舞い(処理)を追加するための設計パターンである。
従来、各クラスにロジックを追加しようとすると、構造そのものを汚染してしまう。
Visitorパターンはこの問題を解消し、構造と振る舞いを分離した拡張性の高いアーキテクチャを構築する。
1. なぜVisitorが必要か?
❌ 構造クラスに処理が混在しがち
class Circle:
def draw(self): ...
def export_json(self): ...
def export_svg(self): ...
→ クラス本来の責務(図形の定義)にフォーマット処理などの副次的責務が混在
→ SRP(単一責任原則)違反
✅ Visitorで「処理を別のオブジェクト」に分離
circle.accept(JsonExporter())
circle.accept(SvgExporter())
→ 構造(図形)に手を加えず、処理(エクスポート)を後から追加可能
2. 基本構造
✅ Elementインタフェース
class Shape:
def accept(self, visitor):
visitor.visit(self)
✅ 具象Elementクラス
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
class Square(Shape):
def __init__(self, length):
self.length = length
✅ Visitorインタフェースと実装
class Visitor:
def visit(self, shape):
raise NotImplementedError
class JsonExporter(Visitor):
def visit(self, shape):
if isinstance(shape, Circle):
print({"type": "circle", "radius": shape.radius})
elif isinstance(shape, Square):
print({"type": "square", "length": shape.length})
class SvgExporter(Visitor):
def visit(self, shape):
if isinstance(shape, Circle):
print(f"<circle r='{shape.radius}' />")
elif isinstance(shape, Square):
print(f"<rect width='{shape.length}' height='{shape.length}' />")
✅ 実行
shapes = [Circle(5), Square(3)]
exporter = SvgExporter()
for shape in shapes:
shape.accept(exporter)
3. Python的な工夫:ダブルディスパッチ風の拡張
Pythonは静的型がないため、visit()
を型ごとに分離しにくいが、以下のように改善可能。
class Visitor:
def visit(self, shape):
method_name = f'visit_{shape.__class__.__name__.lower()}'
visitor = getattr(self, method_name, self.generic_visit)
return visitor(shape)
def generic_visit(self, shape):
raise NotImplementedError
class PrettyPrinter(Visitor):
def visit_circle(self, shape):
print(f"Circle with radius {shape.radius}")
def visit_square(self, shape):
print(f"Square with side {shape.length}")
→ 各型に対応したvisitメソッドを自然に分離・拡張可能
4. 実務ユースケース
✅ 抽象構文木(AST)の走査と解析
- ノード(構文)を変更せず、型ごとの処理(コード生成、静的解析、トランスパイル)を追加可能
✅ 複雑なデータ構造のフォーマット変換
- データ構造の定義を保ったまま、JSON, XML, YAML, HTML など任意の出力を実装
✅ 設計モデル上の振る舞い追加(図形、UIコンポーネント、ファイル構造)
-
validate
,clone
,draw
,calculate_area
などの振る舞いを Visitor として分離
5. よくある誤用と対策
❌ if isinstance()
で分岐が膨張
→ ✅ ダブルディスパッチ形式の visit_xxx
に分離してスケーラブルに
❌ Visitorの処理が肥大化してSRPを侵害
→ ✅ Visitorを複数に分ける(ExporterVisitor, ValidatorVisitor など)ことで責務を分散
❌ Visitor実装がElementに強く依存しすぎる
→ ✅ Visitor
は 構造の使用は最小限にとどめ、意図のみに集中
結語
Visitorパターンとは、“構造を保ちつつ、振る舞いを柔軟に外部から追加する”ための構造的手法である。
- オープンクローズドの原則を保ったまま、責務の分離と後方互換性の確保が可能
- 特に「構造は変えたくないが処理は増やしたい」場面で絶大な威力
- Pythonでも工夫次第で自然かつパワフルに適用可能
Pythonicとは、“拡張性を構造で保証する”ことであり、
Visitorパターンはその保証を設計という文脈で強固に担保する手段である。