0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで実装するVisitorパターン:構造を変えずに振る舞いを追加する拡張戦略

Posted at

概要

Visitor(ビジター)パターンは、
データ構造(オブジェクト群)を変更せずに、新たな振る舞い(処理)を柔軟に追加するための設計パターンである。

データ構造と処理を分離し、アルゴリズムの追加をサブクラスではなく訪問者に担わせることで、
オープンクローズド原則(OCP) を強く保った設計を実現する。


1. なぜVisitorが必要か?

❌ 新しい処理を追加するたびに、すべてのクラスを修正する必要がある

class Circle:
    def render(self): ...
    def export_json(self): ...
    def export_svg(self): ...

機能が増えるたびに、既存クラスが肥大化してしまう


✅ 構造(データ)と処理(アルゴリズム)を分離し、処理はVisitorに委譲する

circle.accept(RenderVisitor())
circle.accept(ExportVisitor())

新しい処理をクラスに加えることなく、独立して追加可能


2. 基本構造

✅ Visitorインターフェース

class Visitor:
    def visit_circle(self, circle): pass
    def visit_square(self, square): pass

✅ Element(構造側の共通インターフェース)

class Shape:
    def accept(self, visitor: Visitor):
        raise NotImplementedError

✅ 具体的なElementたち(Circle / Square)

class Circle(Shape):
    def accept(self, visitor: Visitor):
        visitor.visit_circle(self)

class Square(Shape):
    def accept(self, visitor: Visitor):
        visitor.visit_square(self)

✅ 具体的なVisitor(処理の追加)

class RenderVisitor(Visitor):
    def visit_circle(self, circle):
        print("Circleを描画")

    def visit_square(self, square):
        print("Squareを描画")

class ExportVisitor(Visitor):
    def visit_circle(self, circle):
        print("CircleをJSONでエクスポート")

    def visit_square(self, square):
        print("SquareをJSONでエクスポート")

✅ 使用例

shapes = [Circle(), Square()]
visitor = RenderVisitor()

for shape in shapes:
    shape.accept(visitor)

出力:

Circleを描画  
Squareを描画

3. Python的応用:functools.singledispatch を使った訪問者の簡素化

from functools import singledispatchmethod

class SmartVisitor:
    @singledispatchmethod
    def visit(self, shape):
        raise NotImplementedError

    @visit.register
    def _(self, shape: Circle):
        print("SmartVisitor: Circle処理")

    @visit.register
    def _(self, shape: Square):
        print("SmartVisitor: Square処理")

動的ディスパッチによってクリーンな実装も可能


4. 実務ユースケース

✅ コンパイラ:ASTの各ノードに対するトラバース処理

構文ノードに対して型推論・コード生成・評価などをVisitorで追加


✅ ファイル構造・ドキュメント構造の処理

解析・出力・検索などの処理を分離


✅ UIパーツへのテーマ・スタイル適用

UI構成を変えずに振る舞いだけ差し替え


✅ ゲームオブジェクトに対するエフェクト処理

ゲーム内エンティティに対してイベントやアニメーションを注入


5. よくある誤用と対策

❌ ElementがVisitorに依存しすぎて逆方向の結合が起きる

→ ✅ 訪問を呼び出すだけに留め、処理の実装は完全にVisitorに任せる


❌ Visitorクラスが肥大化して責務が曖昧になる

→ ✅ 用途ごとにVisitorを分割し、粒度を保つ


❌ Visitorの数が増えすぎて管理不能になる

→ ✅ 拡張が必要な領域に限定し、基本的な処理は通常のポリモーフィズムで対応


結語

Visitorパターンとは、“構造を変えずに、振る舞いだけを外から注入する設計”である。

  • 構造を固定したまま、新しい処理を柔軟に追加できる
  • クラスの肥大化を防ぎ、OCP(オープンクローズド原則)を自然に維持
  • Pythonではダブルディスパッチ風の構造や、singledispatch による簡素化も可能

Pythonicとは、“クラスを弄らず、振る舞いを後から優雅に拡張すること”。
Visitorパターンはその開放性の知性を、構造の静けさとして引き出す技法である。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?