23種類のデザインパターンを取り上げるこの連載も、いよいよ最後のパターンとなった。Visitorパターンである。最後となる「訪問者」を紹介しよう。

Visitorとは

ある構造を持つオブジェクトの集合があり、そのそれぞれのオブジェクトに対して処理を行う事を考えてみよう。たとえば、木構造である。木構造では、ノードと呼ばれるオブジェクトが、樹形図を構成している。ここに含まれるオブジェクトを走査し、ノードの種類に応じて処理を行っていくものだ。こういったプログラミングには、構文木を解析するコンパイラや、HTMLのDOMツリーに対する処理などがあるだろう。

このとき、処理を行うのはどのオブジェクトにすべきであろうか。一つの考え方は、ノードオブジェクトに行わせるというものである。自分自身で処理を行う訳だから、筋は通っているだろう。だがこの方法だと、ノードという木構造を表す機能と、コンパイルといった特定の処理が、同一のクラスになってしまう。クラスの再利用性を考えると、構造を表すクラスと、処理を行うクラスは切り離しておきたい。

そこで、オブジェクトの走査を行うときに、それぞれのオブジェクトを訪れて処理を行う「訪問者」となるクラスを作ろう。これがVisitorパターンの考え方だ。Visitorパターンを使うときは、走査を行いながらそれぞれのオブジェクトにVisitorを訪問させてやる。このVisitorの中で、処理を行うのだ。

Visitorパターンの登場人物

Visitorパターンに登場するクラスを紹介しよう。まず、処理を行う対象となるクラスがある。これをElementクラスとしよう。そして、Elementクラスに対して処理を行うクラスを、Visitorクラスとする。

また、Elementの集合を管理する、構造を定義するものもあるだろう。これは、Object Structureと呼ぼう。たとえばObject Strctureの実装として、Iteratorパターンを適用するという事も考えられるだろう。

これらを図示すると、次のようになる。

ここで、VisitorとElementの間に参照関係がないことに注意してほしい。Object Structureの中からElementを走査してVisitorを適用させるのは、ClientまたはObject Structureの仕事になる。

Visitorパターンの実装

では、Objective-CでのVisitorパターンの実装を紹介しよう。

まず、Elementクラスを定義する。Elementクラスには、Visitorを適用させるためのメソッドが必要だ。これを、accept:としよう。具体的なサブクラスとして、ElementAとElementBを定義しておく。

List 1.

@interface Element : NSObject
{
}

// Visitorクラスの訪問を受け付ける
- (void)accept:(Visitor*)visitor;

@end

@interface ElementA : Element
{
}
@end

@interface ElementB : Element
{
}
@end

Elementクラスの実装の前に、Visitorクラスを定義しよう。Visitorクラスには、Elementを処理するためのメソッドを定義する。これを、visitElementA:、visitElementBとしよう。

@interface Visitor : NSObject
{
}

- (void)visitElementA:(ElementA*)element;
- (void)visitElementB:(ElementB*)element;

@end

このvisitElementA:とvisitElementB:を、Elementクラスが呼び出すことになるのだ。

では、Elementクラスの実装を行おう。Elementクラスに定義されているaccept:メソッドの中で、VisitorクラスのvisitElementA:またはvisitElementB:を呼び出すのだ。ElementAとElementBとで、それぞれaccept:をオーバーライドすることで実現しよう。

@implementation ElementA

- (void)accept:(Visitor*)visitor
{
    // Visitorクラスに訪問させる
    [visitor visitElementA:self];
}

@end

@implementation ElementB

- (void)accept:(Visitor*)visitor
{
    // Visitorクラスに訪問させる
    [visitor visitElementB:self];
}

@end

これで、VisitorクラスがElementを処理出来るようになる。VisitorクラスのvisitElementA:、およびvisitElementB:では、それぞれのElementの種類に応じて処理を行うことになる。

これがVisitorパターンの実装となる。次回は、CocoaフレームワークでのVisitorパターンを実現している例を紹介しよう。