前回までは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の仕組みに迫ろう。