0

「addChild」メソッドを持つ「Node」というクラスがあるとします。

@interface Node : NSObject

-(void) addChild:(Node *)n;

このメソッドに応答しない「Sprite」というサブクラスがあります。

@interface Sprite : Node

私はこのようなことができることを知っています:

@implementation Sprite

-(void) addChild:(Node *)n {
    NSLog(@"Don't call addChild on a Sprite!");
}

また

-(void) addChild:(Node *)n {
    NSAssert(NO, @"Don't call addChild on a Sprite!");
}

しかし、サブクラスがこのメソッドに応答しないことを宣言し、コンパイラにエラーをスローさせる方法はありますか? この場合、ランタイム エラーよりもコンパイラ エラーの方がはるかに優れています。

編集

これはリスコフの置換原理に違反していることに気づきました。しかし、Apple のドキュメントには次のように書かれています。

「すべての doesNotRecognizeSelector: メッセージは、通常、ランタイム システムによってのみ送信されます。ただし、プログラム コードで使用して、メソッドが継承されるのを防ぐことができます。たとえば、NSObject サブクラスは、copy または init メソッドを再実装することによって放棄する場合があります。 doesNotRecognizeSelector: メッセージを含めます..."

コンパイル時エラーではなく実行時エラーをスローすることで、この原則に違反することが少なくなる理由がわかりません。

4

4 に答える 4

4

いいえ。

Liskov 置換原則と呼ばれる OOP の原則があります。これは、問題のプログラムの望ましい動作を変更することなく、元のクラスのインスタンスをサブクラスのインスタンスに常に置き換えることができなければならないことを示しています。あなたの場合、ノードが応答することを望んでいますaddChild:。LSP に従うためには、スプライトも応答する必要がありaddChild:ます。

何もできないというわけではありません。スプライトに送信するときに例外をスローさせるaddChild:か、黙って無視させることができますが、実際に必要なのは、ノードとスプライトが同じ親の異なるサブクラスであることだと思います-それをAbstractNodeと呼びましょう。ほとんどのロジックを Node から AbstractNode に移動してから、Node に実装addChild:するだけです。

于 2013-11-07T15:15:44.337 に答える
0

コンパイル時エラーについてはわかりませんが、実行時に試すことができます:

-(void)myMethod {
    [self doesNotRecognizeSelector:_cmd];
}

ただし、親クラスからメソッドを効果的に削除したい場合は、これらのクラスをリファクタリングする必要があると思います。そうでなければ、継承に違反しているようです。

于 2013-11-07T15:09:13.327 に答える
-1

カテゴリを使用してクラスを拡張し、そのカテゴリに addChild メソッドを実装できます。

@interface Node(firstCategory)
-(void)addChild:(Node *)n;
@end

@implementation Node(firstCategory)
// your implementation of addChild
@end
于 2013-11-07T15:05:12.547 に答える