「Delegation」パターンはCocoa Touchアプリケーションでよく使われます。というのも、独自の振る舞いを実装するために、UIApplicationのような複雑なフレームワークオブジェクトのサブクラスを定義し、メソッドをオーバーライドする必要がないからです。
https://developer.apple.com/jp/devcenter/ios/library/documentation/iPhone101.pdf
21ページ
>「Delegation」パターンはCocoa Touchアプリケーションでよく使われます。
Cocoa Touchのアプリケーションを作成するに際に 「Delegation」パターンを使うことが多いです。
>というのも、独自の振る舞いを実装するために、UIApplicationのような複雑なフレームワークオブジェクトのサブクラスを定義し、メソッドをオーバーライドする必要がないからです。
「独自の振る舞いを実装するために」というのは、オリジナルで追加 および修正したい箇所のことです。
通常は フレームワークというものの サブクラスを定義したり
メソッドのオーバーライドをしたりして 作成しますが、
そういう必要性がない ということですね。
オーバーライドというのは フレームワークという プログラムの一部の関数などの
中身を そのまま 変更したりすることです。
継承よりも、委譲 (delegate) の方が柔軟なケースのひとつ。
振る舞いを変えようとしている対象のクラスが複数で、それぞれが継承関係にある場合。
例えば、「図形」を表すクラスがあったとします。
ここに、「影をつける」という機能追加をするときに、継承で実装するとしたら、どうなるでしょう。
継承関係の末端に、「影付き三角形」とか「影付き楕円」を実装することになるでしょう。
図形を描画する、という振る舞いを委譲で実装されている場合、こういうふうに実装できる余地があります。
図形の描画では、さまざまな修飾が行われるかもしれません。
それぞれの修飾の種類ごとに、その実装を、ひとつのクラスにカプセル化できるかもしれません。
修飾の種類が増えたらどうなるでしょう。
「影付き、回転可能な、背景画像が指定できる三角形」なんてのをいくつも作らなくちゃいけない?
「影をつける」、「回転する」、「背景画像をつける」というクラスに、処理を委譲できるかもしれません。
「図形を描画する」という処理を、図形の継承関係から外に出しているのを、
tdoi さんのコメントでは、「プロトコル」と表現しています。
でも、委譲で何でも解決できるでしょうか。
先の図形の例で言うと、描画に必要な情報は、基底の「図形」クラスから取得できないと実装できません。
また、回転させた図形の影はどうかきましょうか。
tdoi さんのコメントでの、「プロトコルの境界を越えて制御することが難しい」というのが、
この辺りの話になります。
委譲に関する質問だったので、委譲のメリットを推し出した書き方をしましたが、
継承には、それに向いたケースがあります。
GoF のパターンで言うと、Decorator や Chain of Responsibility が、委譲ならでは、のパターンで、
Template Method なんかが、継承のメリットを引き出したパターンになります。
拡張されることを意識したフレームワークであれば、サブクラス化してオーバーライドでもそれほど難しさは変わらないとは思います。ただ、必ずしも適切にそのような設計ができるとも限りません。
2012/05/15 23:50:20フレームワークのクラスとして、マウスのクリックを制御するonClickメソッドが実装されていたとします。
フレームワークとしては、onClickメソッドで、クリックされた領域が特定の領域ならウィンドウそのものをドラッグするし、また、別な特定の領域ならウィンドウ内の何かの動きを制御するし、また、他のところであれば、アプリケーションを終了する。というような処理をしていたとします。
ここで、あるアプリケーションでは特定の状況では、終了処理をできないようにしたいとします。
このような変更を想定をしたフレームワークを設計すると、とりあえず思いつくのは、
canFinish();メソッドみたいなのを用意しておいて、親クラスでは、必ずtrueを返しておき、必要があれば、サブクラス化してcanFinishをオーバーライドすればよいという方針です。
ただ、もう一方で、終了可能か判定するDelegateオブジェクトを登録する仕組みにしておくこともできます。この場合は、canFinishの実装が外に出ただけと言ってもいいでしょう。
で、Cocoaは後者のアプローチを使うことが多いよっていうのが、ここでの主張なのかと思います。
オーバーライドするのと、デリゲーションとどちらがよいかはケースバイケースなので、絶対にこちらが優位ということはないです。
強いてデリゲーションを使うメリットを挙げるとすると、プロトコルごとにオブジェクトにまとめることができるためにオブジェクトの意図が明確になるとともに、同様のケースでそのオブジェクトの再利用がしやすい可能性があります。この意図が明確というのは、この例でいえば、アプリケーションの終了だけを意識すればよいということです。親クラスで扱う操作は、アプリケーションの初期化や、その他様々な処理のオーバーライドをすることができますが、そういった要素に対する制御はこのDelegationオブジェクトは持ちえないので、意識しないでよいということです。
逆に言えば、これはサブクラス化の利点とも言ます。デリゲーションではプロトコルの境界を越えて制御することが難しいけれども、サブクラス化をした場合は、その辺りの自由度は高いと言えるでしょうね。自由度と複雑さはある種のトレードオフです。
質問に端的に言うならば、UIapplicationに登録できる特定のDelegationのプロトコルに実装を与えればよく、そのプロトコル以外のことは意識せずに実装できることが利点だと言ってるのでしょうね。
なるほど・・・自由度と明確さのトレードオフなんですね。ふーむ、奥が深いですね。丁寧に説明していただいてありがとうございました。
2012/05/18 08:39:10