前回までは10数回に渡って、Objecgtive-C 2.0の話題を取り上げてきた。ガベージコレクション、プロパティ、Fast Enumerationといった、新規に追加された主要な要素について説明できたので、そろそろ中断されていた元の話題に戻るとしよう。

戻ってくるのは、デザインパターンの話だ。GoFのデザインパターンをObjective-Cで実装するのと同時に、Cocoaフレームワークの中からそれに似たパターンを取り上げて、比較しながら議論するのだ。以前の内容を忘れてしまった人は、本連載の第95回以前を見てほしい。

デザインパターンの再開第一弾で取り上げるのは、Observerパターンだ。他のオブジェクトを監視して、その変更通知を受け取る、というパターンになる。

Observerパターンとは

Observerパターンは、「監視」のためのパターンだ。あるオブジェクトの状態を監視して、何らかの変更が起きたら、それに応じて処理を行うことになる。

たとえば、あるデータをもとにグラフを表示することを考えよう。ここでは、データを表すクラスと、グラフを表示するクラスとを分けることにする。グラフを表示するクラスでは、データを読み込んで、その内容を図示することになる。ここまではいいだろう。問題は、ユーザがなんらかの操作をして、データの内容を変更したときだ。それにあわせて、グラフ表示も変更しなくてはならない。だが、ユーザが毎回グラフ表示の更新を行うようにしたら、それは手間が煩雑になるだろう。

そこで使われるのがObserverパターンだ。グラフ表示のクラスは、データの内容を「監視」する。データは、自分の内容を変更したら、自分を監視しているオブジェクトに対して「通知」を行う。通知を受けたグラフのクラスは、表示内容の更新を行う。これにより、データの内容を変更しただけで、自動的にグラフの更新まで行われることになる。

この仕組みは、複数のグラフ表示を行うときに強みを発揮する。もし、複数のグラフがあるのにObserverパターンを使っていないとしたら、すべてのグラフの更新を手作業で行わなくてはいけないだろう。Observerパターンを使えば、いくつグラフがあってもまったく気にすることはなく、一度のデータ変更で自動的にすべて更新されることになる。

Observerパターンの登場人物

Observerパターンに登場するのは、2つのクラスだ。監視するクラスと、監視されて通知を行うクラスだ。監視するクラスの方を、Observerクラスと呼ぼう。もう一方の監視されるクラスの方は、Subjectクラスとする。

Subjectクラスから説明を行おう。Subjectクラスには、監視するObserverクラスを受け入れるためのメソッドを用意しよう。そして、Observerクラスの参照を保持しておく。もちろん、複数のインスタンスを管理出来るようにする必要がある。

Observerクラスでは、Subjectからの通知を受け取る仕組みが必要だ。そのために、メソッドを1つ用意することになるだろう。

ObserverとSubjectの関係は、次の図のようになる。

Observerパターンの実装

では、Observerパターンを実装してみよう。

まず、Observerクラスからいこう。Observerクラスに必要なのは、通知を受け取る仕組みだ。今回は、updateというメソッドを用意した。通知を受けるときは、Subject側から、このメソッドを呼び出してもらうのだ。

クラスの宣言と実装は、このようになる。

List 1.

@interface Observer : NSObject
{
}

- (void)update;

@end

@implementation Observer

- (void)update
{
    NSLog(@"updated!");
}

@end

updateメソッドの実装では、単にメッセージを表示するだけになっている。アプリケーション特有の処理をやらせたいときは、このクラスのサブクラスを作って、updateメソッドを上書きすることになるだろう。

続いて、Subjectクラスだ。こちらは、グッと複雑になる。

まず、Subjectクラスでは、監視を行っているObserverクラスを管理する必要がある。そこで、_observersというインスタンス変数を用意した。これは、複数のObserverを管理出来るように、NSMutableArray型にした。

そして、監視するObserverを登録および解除する方法が必要である。そのために、attach:とdetach:というメソッドを用意した。引数はObserverインスタンスである。

さらに、通知を行うメソッドも必要だ。これは、notifyという名前にしよう。

これらを実現するクラスの宣言は、次のようになる。

List 2.

@interface Subject : NSObject
{
    NSMutableArray* _observers;
}

- (void)attach:(Observer*)observer;
- (void)detach:(Observer*)observer;
- (void)notify;

@end

そして、このクラスの実装だ。まずはソースコードを見てほしい。

List 3.

@implementation Subject

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }

    _observers = [[NSMutableArray array] retain];

    return self;
}

- (void)attach:(Observer*)observer
{
    [_observers addObject:observer];
}

- (void)detach:(Observer*)observer
{
    [_observers removeObject:observer];
}

- (void)notify
{
    int i;
    for (i = 0; i < [_observers count]; i++) {
        [[_observers objectAtIndex:i] update];
    }
}

@end

初期化メソッドでは、インスタンス変数_observersの初期化を行っている。attach:メソッド、detach:メソッドでは、この_observersへの追加および削除を行っている。

通知を行うnotifyメソッドでは、まず_observersからObserveインスタンスを取り出す。そして、それらのupdateメソッドを呼び出しているのだ。これで、通知が行われることになる。

実際に使用するコードは、次のようになるだろう。

List 4.

    // Observerをインスタンス化する
    Observer*   observerA;
    Observer*   observerB;
    observerA = [[Observer alloc] init];
    observerB = [[Observer alloc] init];

    // Subjectをインスタンス化する
    Subject*    subject;
    subject = [[Subject alloc] init];

    // Observerを追加する
    [subject attach:observerA];
    [subject attach:observerB];

    // 変更を通知する
    [subject notify];

これを実行すると、「updated!」という文字が2回表示される。2つのObserverインスタンスが、それぞれ通知を受けたということだ。

これがObjective-Cでの、Observerパターンの実装だ。次回は、CocoaでのObserverの仕組みに迫ろう。