0
0

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 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に処理が入りすぎる

→ ✅ acceptVisitorの呼び出しに限定すべき


❌ 構造が頻繁に変更されるプロジェクトに無理に適用

→ ✅ Visitorは構造が安定していて、処理が頻繁に追加される場面に適す


結語

Visitorパターンとは、“構造に手を加えず、処理だけを拡張するための訪問設計”である。

  • 処理の拡張をクラス外に独立させ、構造との疎結合を実現
  • 複数の処理(表示/最適化/検証/計算など)を、構造の再利用性を保ったまま外部化
  • Pythonではダックタイピングやマルチディスパッチを活用し、柔軟かつ簡潔にVisitor設計を実現できる

Pythonicとは、“構造と責務を分離して洗練させる”ことであり、
Visitorパターンはその責務の訪問者として、コードの中に拡張性を宿す技法である。

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?