プロトコルを使用する場合は、両方のクラスタイプで共有されるメソッドを2回定義する必要があります。プロトコルは通常、セキュリティを追加して間違いを犯しにくくするために、またはクラス階層にすでに埋め込まれている複数のクラスが共通のメソッドを共有し、この共有を文書化する必要がある場合に、委任パターンなどの特定のパターン用に予約されていますどういうわけか。あるクラスを別のクラスのより特殊なバージョンとして表すことができる場合は、継承する必要があります。
たとえばVehicle
、ゲーム内に、移動など、あらゆる種類の操作を行う方法を知っているクラスがあるとします。クラスを作成したい場合はCar
、おそらくクラスをサブクラス化してVehicle
、そのすべてのメソッド実装を継承できるようにします。それらを大規模に利用するか、独自のバージョンのメソッドを実装します。おそらく、スーパークラスの実装を呼び出す前に、サブクラスに固有のタスクを実行します。サブクラス化は、スーパークラスを何らかの方法で変更しながら、その特性と動作を継承する場合に使用します。これは、インスタンス変数などの追加データをクラスに追加する必要がある場合に特に当てはまります。これは、カテゴリでは追加できないためです(ただし、Class Extension
、多くの場合、一種のプライベートインターフェイスと見なされます)。一般に、サブクラスには、結果としてスーパークラスよりも特殊な目的があります。
プロトコルはまさにそのプロトコルです。それらは、あなたが何かを台無しにしたり忘れたりするのを防ぎ、すべてのオブジェクトが想定どおりに動作することを保証し、クラスが想定どおりに動作していないときにコンパイラ警告をトリガーするためにあります。これは、委任などのパターンにとって重要です。これは、カプセル化を解除して委任がどのタイプのオブジェクトであるかを知る以外に、委任が必要なメソッドを実装することを保証する唯一の方法だからです。たとえば、私のプロジェクトの1つからの以下のコードを見てください。
//SGSprite.h
@protocol SGSpriteDelegate
- (BOOL) animation:(int)animationIndex willCompleteFrameNumber:(int)frame forSprite:(id)sender;
@end
@interface SGSprite : NSObject
@property (nonatomic, assign) id<SGSpriteDelegate> delegate;
@end
//SGViewController.h
@interface SGViewController : UIViewController <SGSpriteDelegate>
//...dreadfully boring stuff
@end
多くのクラスは、SGSprite
テクスチャ2Dクワッドをレンダリングするために私のクラスを利用しています。スプライトがアニメーションの特定のフレームに到達したことを知る必要がある場合があるため、SGSprite
インスタンスはデリゲートのメソッドを呼び出して、特定のフレームに到達したことを通知する必要があります。このクラスのインスタンスへのデリゲートがこのメソッドを実装することを保証する唯一の方法、そして実際、誰かがデリゲートとしてではないオブジェクトを割り当てようとした場合に警告する唯一の方法は、プロトコルを使用することです。デリゲートを単純にプレーンid
にすると、デリゲートでこのメソッドを呼び出すたびに警告が表示されます。これは、その実装が見つからないためです。一方、デリゲートのヘッダーをインポートするか、デリゲートを静的に入力すると、クラス十分にカプセル化されていません。
ほとんどの場合、技術的にプロトコルは必要ありません。通常はプロトコルに準拠するすべてのクラスで、プロトコルなしですべてのメソッドを定義でき、すべてが正常に機能します。ただし、これらの一般的な方法は文書化されていません。したがって、特定のクラスまたは匿名オブジェクトが実装に必要なメソッドを実装していることを知っているというセキュリティを超えて、何が何をどのように実行するかをすばやく判断することもできます。プロトコルは、クラスまたはクラスのインスタンスが何らかのメソッドを実装することを保証する必要がある場合、特にオブジェクトのタイプがクラスをカプセル化したままにする必要がない場合に使用します。