ポリモーフィズム(多態性)について考えてみる。
Java で言えばインターフェース実装、もしくは、メソッドオーバライド、C++ で言えば仮想関数とか。
まぁ早い話が大本の枠を作って、実装は個々のクラス次第ということ。
public interface IWorker { void Work(); } public class Programmer : IWorker { public void Work() { /* コード書いたり */ } } public class Teacher : IWorker { public void Work() { /* 他人に何か教えるとか */ } }
とまぁこんな感じにしといて、どっちも「Worker」だから、
Worker worker = getRandomJob(); worker.Work();
何の職業かは知らなくても、働けって言えば働いてくれる。どう働くかは実装次第という仕様。
Objective-C では、インターフェースは必要ない。だってみんな「Object」であって、メッセージを受けて動くか動かないかだけだから。
@interface Programmer { } - (void)work; @end @interface Teacher { } - (void)work; @end
と、とにかく同じメソッドさえあるのなら
id worker = [self randomJob]; [worker work];
と呼べる。
前に述べた通り、型安全などないので、randomJob の返り値が、work メッセージレセプタ(便宜上メソッドを分けて呼称)を実装しているかどうかは問題ではないし、null(nil) であっても特に問題はない。*1
とはいえ、このような書き方で、実装してるかどうか相手任せで、実行時例外を出されても困る。そんなとき、3種類の手段がある。
- protocol を作成、実装する。
- 非形式プロトコルを作成する。
- forwardInvocation で処理してしまう。
最初の方法は、Java/C++/C# のインターフェースと何ら変わりがない。なぜこのようなものがあるかというと、シリアライズされた際の転送速度を稼ぐとか、アクセス速度を稼ぐ狙いがある。
書くのが面倒だったり、必ず実装とかだるいので滅多に書かない。書くとするとこんな感じ。
@protocol Worker - (void)work; @end @interface Programmer : NSObject <Worker> { } - (void)work; @end
次に非形式プロトコルだが、これはもっと単純な実装になる。
何の事はない、共通の基底クラスにカテゴリを使ってメソッドを作ってしまう。これは、実装を変更するかどうかを、継承先で任意にできる。
@interface NSObject(Worker) - (void)work; @end
NSObject を継承するすべてのオブジェクトは、work メソッドがある事が保証される。
最後は、invocationForwarding を使う方法だ。ただし、この方法は Cocoa ありきか、、、。
@interface Neet : NSObject { id other; } @end @implementation Neet - (NSMethodSignaure*)methodSignatureForSelector:(SEL)selctor { // other がメソッド持ってたら丸投げする。 if ([other respondsToSelector:selector]) { return [other methodSignatureForSelector:selector]; } return [super methodSignatureForSelector:selector]; } - (void)forwardInvocation:(NSInvocation*)invocation { // 問答無用で other に丸投げ。テラヒドス [invocation invokeWithTarget:other]; }
という感じ。
*1:実装されていなければ例外が出るけど