Strategyとは

Strategyは、アルゴリズムをカプセル化するために使われるパターンだ。ある処理に対して複数のアルゴリズムが存在する場合、それぞれのアルゴリズムをクラスとして定義し、切り替えながら使う事で、拡張性を高く保つ事ができる。

GoF本の分類によると、Strategyパターンには2つのクラスが登場する。1つは、アルゴリズムを実現するStrategyクラスだ。Strategyクラスは抽象クラスとして、そのサブクラスに具体的なアルゴリズムを実装する。もう1つは、そのクラスを利用するContextクラスだ。

Startegyクラスについては、正直なところ、これ以上特筆する事がないようにも感じる。クラスを切り替える事によってアルゴリズムを変更するというのは、オブジェクト指向プログラミングにおいてはある程度定石とも言えるものだろう。Strategyが提案するパターンには、目新しさはそれほど感じられない。

ということで、早速Cocoaで使われているStrategyパターンを探してみる事にしよう。

プロトコルをサポートするNSURLProtocol

Cocoaでは、ネットワークアクセスをサポートしている。このサポートにおいて大事な事は、様々なプロトコルに対応し、かつ新しいプロトコルに柔軟に対応できる事だろう。Cocoaでは、標準でhttp、https、fileといったプロトコルに対応し、かつ拡張可能な設計になっている。

そのために使われているのが、NSURLProtocolというクラスだ。このクラスは、ネットワークプロトコルをサポートするために用意されている。Objective-Cのプロトコルとは違うので、注意してほしい。

NSURLProtocolはネットワークプロトコルのための親クラスとなり、これのサブクラスを作る事で様々なプロトコルをサポートする。上書きする必要のあるメソッドには、まずcanInitWithRequest:がある。これは、あるリクエストに対して、このプロトコルが対応出来るどうかを返す。

List 1.

+ (BOOL)canInitWithRequest:(NSURLRequest*)request

引数として渡されるのは、NSURLRequestクラスのオブジェクトである。このクラスから、リクエストのURLを取得できるので、そのスキームをチェックすれば、対応可能なリクエストかどうかが分かるだろう。

このメソッドでYESを返すと、ネットワークアクセスを開始するために、そのクラスがインスタンス化される。使われる初期化メソッドは、initWithRequest:cachedResponse:client:である。

List 2.

- (id)initWithRequest:(NSURLRequest*)request cachedResponse:(NSCachedURLResponse*)cachedResponse client:(id<NSURLProtocolClient>)client

第一引数のrequestは、先ほどのネットワークリクエスト。第二引数のcachedResponseは、もしキャッシュされたレスポンスがあるならば、それが渡ってくる。必要に応じてキャッシュを使うか、ネットワークアクセスを発生させるかを決定する。第三引数であるclientは、アクセスした結果のレスポンスを受け取る相手である。これについては、後述しよう。

初期化が終わったら、ネットワークアクセスを開始する。これに使われるのは、startLoadingというメソッドである。

List 3.

- (void)startLoading

これをトリガーとして、それぞれのプロトコルに則ってアクセスを開始する。

レスポンスを受け取るNSURLProtocolClient

ネットワークアクセスの結果レスポンスを得たら、それをクライアントに通知する。そのために使われるのが、NSURLProtocolClientプロトコルになる。ここでいう「プロトコル」は、Objective-Cのプロトコルになる。

このプロトコルでは、レスポンスを受け取るためのメソッドが定義されている。その一部を紹介しよう。次のようなものがある。

List 4.

- (void)URLProtocol:(NSURLProtocol*)protocol didReceiveResponse:(NSURLResponse*)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy
- (void)URLProtocol:(NSURLProtocol*)protocol didLoadData:(NSData*)data
- (void)URLProtocolDidFinishLoading:(NSURLProtocol*)protocol

まず、アクセスしてレスポンスを受け取ったら、URLProtocol:didReceiveResponse:cacheStoragePolicy:を呼び出す。これでレスポンスをクラアイントに通知する。

その後、サーバからデータを取得した場合は、URLProtocol:didLoadData:を呼び出す。受け取ったデータを、順次クライアントに送付する。それらはクラアイント側で1つにまとめられるだろう。

アクセスが終了したら、URLProtocolDidFinishLoading:を呼び出す。これをトリガーとして、クライアントはレスポンスデータに関する処理を開始する訳だ。

StrategyパターンとしてのNSURLProtocol

NSURLProtocolを使ったネットワークアクセスは、Strategyパターンと捉えることが出来るだろう。Strategyクラスになるのが、NSURLProtocolとそのサブクラスだ。リクエストのプロトコルの種類によって、サブクラスを切り替える事で自在にネットワークアクセス出来る。

では、Contextにあたるクラスは何になるかというと、NSURLConnectionというクラスがある。このクラスが、リクエストに応じたNSURLProtocolクラスの初期化と、レスポンスの受け手を担当している。

これが、CocoaにおけるStrategyパターンの一例となるだろう。