Compositeパターンとは
Compositeは、階層関係を実現するためのパターンだ。親となるオブジェクトは、同じクラスの子オブジェクトを持つことができる。
例によって、GoF本の例から見てみよう。取り上げられているのは、ドロー系のグラフィックツールだ。このツールでは、線、四角、テキストといったプリミティブを組み合わせていくことで、画像を作り上げていく。だが、これらのオブジェクトに求められる機能は、絵を描くことだけではない。他のプリミティブを内部に配置する、コンテナとしての機能も求められるのだ。
このような要求に応えるために、まず描画機能を定義するGraphicsというプリミティブを定義する。それを継承する形で、Line、Rectangle、Textといったクラスを作っていく。それに加えて、コンテナとなるPictureクラスを、これもまたGraphicsのサブクラスとして定義する。Pictureクラスは、子オブジェクトしてGraphicsの配列を持つことができる。これにより、階層関係が実現できる。
ポイントは、PictureはGraphicsのサブクラスなので、Picture自身を子オブジェクトとして保持できることだ。この再帰的な仕組みにより、柔軟な階層構造を実現できることになる。
Compositeパターンの登場人物
このようなCompositeパターンを作るための、登場人物を定義しよう。
まず、階層関係の基本となるクラスとして、Componentを定義する。Componentを継承したクラスが、階層関係の中に登場する。Componentでは、このモデルで必要となる基本的なメソッドを定義する。Componentを継承したクラスで、これらをオーバーライドすることになる。
Componentを継承したクラスは、2つ用意しよう。1つは、階層関係の末端となるオブジェクト。これをLeafとする。もう1つは、コンテナとして他のComponentを持つことができるクラス。これがCompositeとなるわけだ。
これらの関係をまとめると、次のようになるだろう。
![]() |
Compositeは、子となるComponentを複数持つことができる。この中に、Composite自身を持つことができるのが重要なところだ。
Compositeパターンの実装
では、このCompositeパターンをObjective-Cで実装してみよう。
まずは、Componentからだ。ComponentObjectというクラスを定義する。本当はComponentクラスにしたかったのだが、Cocoa環境下ではすでにこの名前は使われているので、ComponentObjectとした。
ComponentObjectでは、基本となる操作メソッドを定義する。ここでは、operationというメソッドを定義した。
List 1.
@interface ComponentObject : NSObject
{
}
- (void)operation;
@end
次に、Componentを継承したクラスを定義しよう。まず、Leafだ。Leafは階層関係の末端となるので、子は持たない。目的は、独自のoperationメソッドを実装することになる。
List 2.
@interface Leaf : ComponentObject
{
}
@end
@implementation Leaf
- (void)operation
{
// operationの実装
}
@end
そして、Compositeクラスを実装する。このクラスもComponentを継承する。そして、コンテナとしての役割を果たすために、子となるオブジェクトを保持できるようにしよう。そのためには、インスタンス変数として、配列であるNSMutableArrayを持つ事にする。さらに、子オブジェクトにアクセスするためのメソッドも用意しよう。add:、remove:、childAtIndex:といったメソッドを定義しておく。
List 3.
@interface Composite : ComponentObject
{
NSMutableArray* children;
}
- (void)add:(ComponentObject*)component;
- (void)remove:(ComponentObject*)component;
- (id)childAtIndex:(int)index;
@end
Compositeに必要なメソッドを実装していこう。まず、子となるオブジェクトにアクセスするためのメソッドだ。これらでは、子を保持している配列を操作することになる。
List 4.
- (void)add:(ComponentObject*)component
{
[children addObject:component];
}
- (void)remove:(ComponentObject*)component
{
[children removeObject:component];
}
- (id)childAtIndex:(int)index
{
return [children objectAtIndex:index];
}
そして、Component独自の処理を行うoperationメソッドだ。ここには、Composite独自の処理を記述する。それに加えて、保持している子オブジェクトにもoperationメソッドを伝播させる必要がある。
List 5.
- (void)operation
{
// Composite独自の処理
...
// 子のoperationを呼び出す
NSEnumerator* enumerator;
ComponentObject* component;
enumerator = [children objectEnumerator];
while (component = [enumerator nextObject]) {
[component operation];
}
}
Objective-CでのCompositeパターンの実装は、このようなものになるだろう。次回は、Cocoaでの実装の紹介に移ろう。もちろん、CompositeといえばModel/View/ControllerのViewになる。