Objective-Cによるデザインパターンの探訪。ここまでで、生成に関するパターン、構造に関するパターンを掘り下げてきた。今回からは、いよいよ振る舞いに関するパターンに入っていく。Ccooaでは、このパターンに分類される構造が数多く出てくるだろう。

まずは軽いジャブとして、Iteratorパターンを取り上げよう。

Iteratorパターンとは

Iteratorパターンは、多くの言語やフレームワークで取り入れられているパターンだ。最近のプログラミング環境を触った事のある人ならば、おそらく使ったことがあるだろう。だが確認の意味もこめて、一応説明しておこう。

Iteratorは、オブジェクトの集合に対して順にアクセスするときに、その集合の内部構造によらない統一的な手段を与えるものだ。

例として、まず配列を考えてみよう。配列は、オブジェクトを連続して並べるタイプの集合だ。インデックスを指定して、それぞれのオブジェクトにアクセスすることができる。従って、配列に含まれるオブジェクトを順に取り出すには、for文を回してやるのが一番簡単だ。

だが、オブジェクトの集合は配列だけではない。たとえば、連結リストがある。これは、オブジェクトを含むノードをリストとして結ぶものだ。このタイプの集合では、for文のようにインデックスを指定するアクセス方法では、パフォーマンスが悪くなる。また、木構造もある。木構造のすべてのオブジェクトにアクセスするには、for文ではない別の方法が必要だ。

このように、プログラミングでは様々なタイプの集合を扱わなくてはいけないので、単純なfor文では対応できない。だが、オブジェクトの集合に対して行いたい操作は、オブジェクトを順に取り出すという共通のものだ。そこで、この統一的な操作を与えるクラスを定義できないだろうか。この問題に取り組むのが、Iteratorパターンだ。

Iteratorパターンの走査メソッド

Iteratorパターンでは、オブジェクトを順に取り出すために、つまりオブジェクトの走査を行うためにIteratorクラスを定義する。このIteratorクラスが持つメソッドだけで、走査が行えるはずだ。

GoF本の例を見てみよう。Iteratorクラスには、次の4つのメソッドが定義されている。

List 1.

First()
Next()
IsDone()
CurrentItem()

First()は、操作位置を先頭に持っていくもの。Next()は、操作位置を次ぎに移すもの。IsDone()は、走査が完了したかどうかをしめすもの。そして、CurrentItme()は、現在のアイテムを取り出すものだ。

これらを使えば、走査を行うソースコードは次のようになるだろう。C++ライクな文法で書いてみた。

List 2.

Iterator*   iterator;

// iteratorを取得する
...

iterator->First();
while (!iterator->IsDone()) {
    Item* item;
    item = iterator->CurrentItem();

    iterator->Next();
}

Iteratorパターンの実装

こんなIteratorを、Objective-Cで実装してみよう。ここでは、Cocoaの配列クラスであるNSArrayのためのIteratorクラスを作ってみる。もちろん、CocoaにはIteratorパターンのためのクラスがあり(NSEnumeratorクラス)、通常はそちらを使う。あえて、自分でIteratorを作ってみるのだ。

まずはIteratorクラスの定義だ。次のようにしてみた。

List 3.

@interface Iterator : NSObject
{
    NSArray*        _array;
    unsigned int    _index;
}

- (id)initWithArray:(NSArray*)array;

- (void)first;
- (void)next;
- (BOOL)isDone;
- (id)currentItem;

@end

インスタンス変数として、配列と、現在の操作位置をあらわす_indexを用意した。メソッドは、初期化のためのinitWithArray:。それに加えて、Iteratorのための4つのメソッドだ。

次に、実装をみてみる。まずは、初期化のためのinitWithArray:だ。

List 4.

- (id)initWithArray:(NSArray*)array
{
    self = [super init];
    if (!self) {
        return nil;
    }

    _array = array;
    _index = 0;

    return self;
}

通常の初期化処理のあと、インスタンス変数を初期化している。_indexを0にすることで、操作位置が先頭になる。

続いて、走査のためのメソッドを実装しよう。

List 5.

- (void)first
{
    _index = 0;
}

- (void)next
{
    _index++;
}

- (BOOL)isDone
{
    return _index >= [_array count];
}

- (id)currentItem
{
    if ([self isDone]) {
        return nil;
    }

    return [_array objectAtIndex:_index];
}

一つ一つののメソッドでは、それほど難しい事はやっていない。firstとnextでは、操作位置を動かしている。isDoneでは、_indexの値と配列の大きさを比較して、走査が完了しているかどうかチェックしている。そして、currentItemでは、NSArrayのobjectAtIndex:メソッドでオブジェクトを取り出している。

では、このクラスを使ってみよう。次のように書けば、すべてのオブジェクトを取り出せるはずだ。

List 6.

NSArray*    array;
array = [NSArray arrayWithObjects:
        @"Happy", @"Cocoa", @"Programming", nil];

Iterator*   iterator;
iterator = [[Iterator alloc] initWithArray:array];
while (![iterator isDone]) {
    id  item;
    item = [iterator currentItem];
    NSLog(@"%@", item);

    [iterator next];
}

ちなみに、この実装ではあまりパフォーマンスはよくない。最後のオブジェクトをとりだすところで、objectAtIndex:を使ってしまっているからだ。これでは、for文でインデックスベースのアクセスをしているのと変わらない。本来ならば、NSArrayの内部構造に直接アクセスするような仕組みが必要だろう。

『たのしいCocoaプログラミング』発売中

さて、本連載に興味をお持ちの方に1つ告知をさせていただきたい。この度、拙著『たのしいCocoaプログラミング』が発売となった。

この本は、完全な初心者向けに書いた、Cocoaプログラミングの本である。プログラミング知識がまったくない人でも、Cocoaを使ってアプリケーションを動かすことができるようになる(はずだ)。そのために、Objective-Cはもちろん、C言語の解説も含まれている。

プログラミングを始めてみたい方や、Cocoaプログラミングに興味を持っている方は、ぜひ手に取ってみてほしい。

提供:毎日キャリアバン ク

毎日キャリアバンクではITエンジニア出身のキャリアコンサルタントで形成する IT専門のチームを編成し、キャリアに応じた専任コンサルタントがご相談を承り ます。キャリアチェンジから市場価値の可能性、ご収入などの相談から面接のア ドバイスまでお気軽にご相談ください。求人情報誌や転職情報サイトなどで一般 に公開されていないような「急募求人案件」も随時ご紹介が可能です。まずはご 登録ください!