2
3

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(ビジター)パターンは、
データ構造(オブジェクト群)の定義を変更することなく、
新しい振る舞い(操作)を追加可能にするための構造的デザインパターンである。

この設計により、「構造」と「振る舞い」を分離し、
オブジェクトに新たな処理を注入することが可能となる。


1. なぜVisitorが必要か?

❌ オブジェクトクラスに振る舞いが集中する設計

class File:
    def print(self):
        ...
    def export(self):
        ...

→ ファイルの構造に新しい処理を加えるたびに、クラスの肥大化と責務の分散が発生


✅ 振る舞いはVisitorクラスに切り出して注入する

class PrintVisitor:
    def visit_file(self, file): ...

構造定義を触らずに振る舞いの追加・変更が可能


2. 基本構造

✅ Element(訪問される側)

class Element:
    def accept(self, visitor):
        raise NotImplementedError
class File(Element):
    def __init__(self, name):
        self.name = name

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

✅ Visitor(振る舞いを定義する側)

class Visitor:
    def visit_file(self, file):
        raise NotImplementedError
class PrintVisitor(Visitor):
    def visit_file(self, file):
        print(f"[Print] File name: {file.name}")

✅ 使用例

file = File("document.txt")
visitor = PrintVisitor()
file.accept(visitor)  # → [Print] File name: document.txt

3. Python的応用:ダブルディスパッチの活用

class Folder(Element):
    def __init__(self, children):
        self.children = children

    def accept(self, visitor):
        visitor.visit_folder(self)
class CountVisitor(Visitor):
    def __init__(self):
        self.count = 0

    def visit_file(self, file):
        self.count += 1

    def visit_folder(self, folder):
        for item in folder.children:
            item.accept(self)
f1 = File("a.txt")
f2 = File("b.txt")
folder = Folder([f1, f2])

visitor = CountVisitor()
folder.accept(visitor)
print(visitor.count)  # → 2

構造を歩きながら異なる型に応じた処理を実行


4. 実務ユースケース

✅ 抽象構文木(AST)のトラバースと評価

→ 構文要素ごとに処理(例:プリント/変換/最適化)を注入


✅ ファイルシステムやXMLの構造解析

→ ファイル/フォルダ/属性などのツリー構造に対して統一されたアクセス


✅ データモデルへの複数操作(表示、エクスポート、検証)

→ データ構造を変更せず表示・保存・検査の各処理を独立して管理


✅ ゲームエンジン内のエンティティ評価処理

→ 位置計算、AI判定、エフェクト適用など一括処理を分離して追加可能


5. よくある誤用と対策

❌ VisitorがすべてのElementに依存

→ ✅ Visitorを分割して関心の分離を保つ(Single Responsibility Principle)


❌ accept() メソッドの未実装

→ ✅ 各Element型に明示的に accept(visitor) を実装し、動的な振る舞い注入を保証


❌ Visitorにロジックが集中しすぎる

→ ✅ Visitorは操作の集合体であって、構造の責任を持たない


結語

Visitorパターンとは、“構造に手を加えず、振る舞いだけを柔軟に追加する”ための構造的技法である。

  • 型ごとの処理をVisitorに集約することで、拡張性と保守性を同時に満たす
  • 実行対象が複数種類のオブジェクトを持つ場合に、構文的に洗練されたディスパッチ処理を実現
  • Pythonにおいても動的ディスパッチの仕組みとクラス構造の活用により、自然かつ強力なVisitor設計が可能

Pythonicとは、“構造と振る舞いを分離し、明快な責務で再構成する”ことであり、
Visitorパターンはその設計的透明性と柔軟性をコードに浸透させる知的戦略である。

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?