ようこそ。睡眠不足なプログラマのチラ裏です。

デザインパターン第8回「Visitorパターン」

独りよがりなデザパタ講座も、ようやくGoFデザパタ23種類の内の3分の1を超えました。
今回はGoFの23種類のデザインパターンの中でも、最大級のクラス図で表され、
比較的複雑な部類である「Visitorパターン」をぬるーく解説します。
このパターンの考え方を知らないのであれば、このパターンを学ぶことで、
オブジェクト指向のエッセンスをより感じられるのではないかと思う。



Visitorパターンは、データ構造と振る舞いを、異なる別々のクラスに扱わせることにより、
オペレーション(振る舞い)の変更や追加、拡張を可能にする設計パターンです。
Visitor(訪問者)となるオブジェクトが、対象となるオブジェクト構造の中を巡回して適切な処理を行います。
例えるなら、if文やswitch(Select Case)文となる分岐を、ポリモーフィズムで表現したパターンと言えるかもしれない。


■Visitorパターンの概要■
このパターンでは、データ構造とオペレーション(振る舞い)を分離します。
つまり、データ構造に変更を加えずに、新しいオペレーションを追加できることを目的としたパターンです。


Visitorとは、英語で「訪問者」を意味します。
処理対象オブジェクトは、振る舞いを訪問者であるVisitorオブジェクトに委譲することで、
オペレーションの追加や変更を容易にします。
処理対象オブジェクトは、Visitorオブジェクトを受け入れる役割を果たすAcceptメソッドを実装し、
そのメソッドでは、受け入れたVisitorオブジェクトの、メソッドに自身を受け渡して再び呼び出します。
このように、データ構造側のポリモーフィズムを使ったディスパッチと、
振る舞い側のポリモーフィズムを使ったディスパッチの、2回のディスパッチが行われていることから、
Visitorパターンは、しばしばダブルディスパッチパターンとも呼ばれます。


Visitorパターン(ダブルディスパッチ)のメリット、デメリット

Visitorパターンは、データ構造と振る舞いを分離するパターンです。
振る舞いを訪問者クラスに封じ込め、受け入れ側のクラスの振る舞いは、
渡された訪問者クラスに委譲されます。


受け入れ側のクラスの中に直接処理を書くこともできるが、
対象となるクラスの種類や、条件分岐が非常に多いときにそれをやってしまうと、
if文やswitch(Select Case)文が複雑になり、メンテナンス性を失います。
また、乱雑化したif文のオンパレードは、ケアレスミスやテスト漏れを招き、バグの温床ともなり兼ねない。
つまり、拡張や変更の多いアジャイル開発においては、Visitorパターンの考え方が有効となってきます。


ただし、このパターンは、振る舞いに対する拡張性・柔軟性には富んでいると言えるが、
逆にデータ構造に対する拡張性・柔軟性にはすぐれていない点には、設計時に注意する必要がある。
データ構造に変化が生じるような場合のメンテナンス性は皆無と言っていいかもしれない。



Visitorパターン(ダブルディスパッチ)の利用について

Visitorパターンでは、受入オブジェクト(Element)に対する処理を、訪問者オブジェクト(Visitor)に委ねます。
受入オブジェクトに対して、this(Me)キーワードを使って、訪問者オブジェクトは自分自身をすべて引き渡します。
そして、訪問者のオブジェクトを受け取った受入オブジェクトは、訪問者オブジェクトのメソッドを呼び出して処理を実行します。
これは、データ構造を持つ受入オブジェクトと、その処理を行う訪問者オブジェクトとを分離することを目的としています。
そうすることでデータ構造を持つオブジェクトをシンプルに保ち、そのデータ構造に対する振る舞いに対して柔軟性をもたせます。



上記、「Visitorパターンの概要」でも示したとおり、
このパターンでは、データ構造側のポリモーフィズムを使ったディスパッチと、
振る舞い側のポリモーフィズムを使ったディスパッチが、2重に行われています。
このダブルディスパッチの考え方自体は、とてもシンプルなのだが、
オブジェクト指向的な考え方に慣れていない場合は、なかなかピンとこないというのもまた事実だろう。
設計理論で理解するというよりも、実装で理解した方が分かり易いと思われるので、
あまりピンとこないという方は、まずは実際に実装をしてみることをお勧めする。



オブジェクト指向的に考えてクラスを設計する場合は、機能とデータをまとめてカプセル化するのが一般的だが、
機能の追加が頻繁に発生することが想定される場合は、Visitorパターンの考え方を適用した方よい。
機能とデータを一緒にカプセル化すべきなのか、機能とデータを分離してカプセル化すべきなのかが
このパターンを適用すべきかどうかのポイントとなる。



また、「Visitorパターンの概要」では、データ構造とオペレーション(振る舞い)を分離し、
データ構造に変更を加えずに、新しいオペレーション(振る舞い)を追加できることを目的としたパターンであると述べたが、
Visitorパターン(ダブルディスパッチ)は、オブジェクトそのものに特定の判断を委ねることで、
if文やswitch(Select Case)文による処理の複雑化を避けようとする場合にも、有効となるだろう。
以下に、その一例を示したオマケを用意しました。多くの判定が必要となる処理を、
Visitorパターンを適用することにより、比較的スマートに表現しています(´・ω・`)


結構面倒くさかったけど作ったオマケ
Visitorパターン - VB.NET - 雑なサンプルソース右クリック保存



これは余談だが、Visitorパターンを説明するにあたって、
Compositeパターンと併用して利用することを前提とした解説が多い傾向があるようだが、
Visitorパターンの用途はそれだけに限った話ではない。ということは理解しておく必要があるだろう。