PHP7でデザインパターン入門13/23 Visitorパターン

Visitor パターンとは

アルゴリズムをオブジェクトの構造から分離するためのデザインパターンである。分離による実用的な結果として、既存のオブジェクトに対する新たな操作を構造を変更せずに追加することができる。

Visitor パターン - Wikipedia

データ構造の中にたくさんの要素が格納されており、各要素に対して何かの処理をする必要があるとする。このときのその処理のコードはどこへ書くべきか。データ構造を表しているクラスへ処理を書いた場合、もし処理が1種類と限らなければ、新しい処理が必要になるたびにデータ構造を修正することになる。Visitorパターンではデータ構造と処理を分離する。

example

サンプルコード

訪問者が渡り歩くデータ構造として、Compositeパターン*1で登場したファイルとディレクトリで構成されたデータ構造の中を訪問者が渡り歩きファイルの一覧を表示するプログラムを作成する。

github.com

Visitor

ファイルやディレクトリを訪れる訪問者を表す抽象クラス

Element

Visitorクラスのインスタンスを受け入れるデータ構造を表すクラス

ListVisitor

Visitorクラスのサブクラスで、ファイルやディレクトリの一覧を表示するクラス

Entry

FileとDirectoryのスーパークラスとなる抽象クラス

File

ファイルを表すクラス

MyDirectory

ディレクトリを表すクラス

Main

動作テスト用

実行結果

$ php Main.php
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)

Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

役割

Visitor役

データ構造の具体的な要素ごとに訪問する visit()メソッドを宣言する。サンプルコードにおけるVisitorクラス。

ConcreteVisitor役

Visitor役のインターフェースを定義する。visitXXX()メソッドを実装し、ここのConcreteElement役ごとの処理を記述する。 サンプルコードにおけるListVisitorクラス。

Element役

Visitor役の訪問先を表す役。訪問者を受け入れるaccept()メソッドを宣言する。acceptメソッドにはVisitor役が渡される。 サンプルコードにおけるElementインターフェース。

ConcreteElement役

Element役のインターフェースを実装する役。サンプルコードにおけるFile, MyDirectoryクラス。

ObjectStructure役

オブジェクト構造。Element役の集合を扱う役。ConcreteVisitor役が個々のElement役を扱えるようなメソッドを備えている。 サンプルコードにおけるMyDiretoryクラス(MyDirectoryは1人2役)。

まとめ

ダブルディパッチ

elementはvisitorをacceptし、visitorはelementをvisitしている。VisitorパターンではConcreteElement役とConcreteVisitor役の組によって実際の処理が決定する、これを一般にダブルディパッチと呼ぶ。

Open-Closed Principle

OCP。

  • 拡張(Extension)については開かれている
  • 修正(Modification)については閉じられている

既存のクラスを修正せずに拡張できるようにすべき、という方針。 デザインパターンの目的はこのような仕組みを提供することにある。

ConcreteVisitor役の追加は容易

新しいConcreteVisitor役を追加する際に、ConcreteElement役を修正する必要はない

ConcreteElement役の追加は困難

サンプルコードで、EntryクラスのサブクラスとしてDeviceクラスを追加したいとする。DeviceクラスはFileクラスとMyDirectory区rスの兄弟にあたる。そのとき、Viistorクラスには新しいvisitDevice()メソッドを追加する必要が生じる。そしてvisitorクラスのサブクラス全てに新たにvisitDevice()メソッドを実装しなければいけなくなる。

関連パターン

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門