3

基本的に、私は次のようなクラス階層を持っています。

NSObject
  MySpecialController
    MyExtraSpecialController

これらのそれぞれにinitメソッドがあり、各実装はsuper最初に呼び出して、スーパークラスが最初にそれ自体を初期化するようにします。より良い用語がないため、各クラスはそのスーパークラスの動作を「強化」していると言えます。

しかし、スーパークラスの動作を完全に「置き換え」たいとしましょう(特定のアプリにさらに特化したいのですが、一般的な再利用可能なスーパークラスを乱雑にすることはありません。したがって、スーパークラスについての深い知識があると想定されます。 )。私がしたい実際の変更は、プロパティをより具体的なクラスタイプの1つに置き換えることです。これを完全に実装するには、適切なクラスのinitインスタンスをインスタンス化するメソッドが必要です。widgetしたがって、をインスタンス化する場合MySpecialController、そのwidgetプロパティはMySpecialWidget;型である必要があります。しかし、私がインスタンス化する場合MyExtraSpecialController、それwidgetはタイプでなければなりませんMyExtraSpecialWidget

//MySpecialController:

@interface MySpecialController : NSObject

@property (strong, nonatomic) MySpecialWidget *widget;

@end

@implementation MySpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [MySpecialWidget new];
    }
}

@end


//MyExtraSpecialController:

@interface MyExtraSpecialController : MySpecialController

@property (strong, nonatomic) MyExtraSpecialWidget *widget;

@end

@implementation MyExtraSpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [MyExtraSpecialWidget new];
    }
}

@end

現在、これは機能するという意味でMySpecialController機能し、パブリックAPIを使用するすべての人が使用できます。またMyExtraSpecialController、機能し、スーパークラスの動作について何も想定していないため、関心の分離を適切に実行します。これは、フレームワークまたはライブラリクラスから作成するサブクラスのタイプです。堅牢で控えめです。

ただし、実際に発生するのは、の新しいインスタンスを作成するとMyExtraSpecialController、そのスーパークラスが最初にをインスタンス化しMySpecialWidget、次にそのインスタンスの割り当てをすぐに解除して、のインスタンスに置き換えますMyExtraSpecialWidget。確かにこれは機能しますが、私はスーパークラスについての深い知識を持っているので(つまり、基本的にそのinitメソッドが何をするのかを正確に知っているので、最初に呼び出す必要なしに安全に置き換えることができます)、この問題を回避してインスタンス化するだけです単一のウィジェット(ウィジェットの作成は非常に費用がかかり、時期尚早の最適化ではありません)。だから私はスーパーの実装を完全に置き換えてウィジェットを作成せず、私の親密な知識に基づいてそれが行う他のすべてを置き換えたいと思っていますが、これが重要です、私はまだ呼び出したいですinitinit置き換えられたクラスの「スーパークラス」メソッド(NSObjectこの場合)が何をするのかわからないため、チェーンのさらに上流にあります。これは、私が詳しく知らないクラスであるためです。

頭に浮かぶ当面の解決策は、Objective-C動的ランタイムを使用して祖父母インスタンスを取得し、そのインスタンスを呼び出すだけですinit(必要に応じてチェーンの呼び出しを処理します)。これにより、superをバイパスします。しかし、私がそのようなことをしようとしているときはいつでも、より良いアプローチがあるかどうか常に疑問に思います-概念的に言えば、つまり、スーパークラスのメソッドを拡張するのではなく置き換えることです。ある?

4

3 に答える 3

3

self.widget関数からのインスタンス化を削除し、init代わりにカスタムの「レイジー」ゲッター関数を実装できます。

- (MySpecialWidget *)widget
{
     if (_widget == nil) {
         _wigdet = [MySpecialWidget new];
     }
     return _widget;
}

次に、サブクラスでこのメソッドをオーバーライドできます。ウィジェットはへの最初のアクセス時に作成されself.widget、スーパークラスまたはサブクラスのゲッターが呼び出されます。

于 2013-03-08T10:51:13.397 に答える
1

これを解決する簡単な方法の1つは、ウィジェットを作成するためのフックを作成することです。

@implementation MySpecialController

-(id)init {
    if (self = [super init]) {
        self.widget = [self makeWidget];
    }
}

- (MySpecialWidget*) makeWidget
 {
     [MySpecialWidget new];
@end

次に、サブクラスをオーバーライドmakeWidgetして。を返すことができますVerySpecialWidget。これは、クライアントにこれらのウィジェットについて知られたくない場合に意味があります。

あなたのシナリオでは、クライアントがウィジェットについて何かを知っている可能性があります。たとえば、VerySpecialWidgetを取得するためにVerySpecialControllerが必要です。その場合は、クライアントにウィジェットを選択させることができます。

 [MySpecialController initWith: [MyVerySpecialWidget new]];

ウィジェットがサブクラスを作成するための主要な力である場合、どちらのアプローチでも、そもそもサブクラスを発芽させる必要がなくなる可能性があります。

2番目のアプローチには、単体テストを容易にするという追加の利点があります。MySpecialControllerを作成して、ダミー、スタブ、またはモックを大騒ぎせずに渡すことができます。

 [MySpecialController initWith: [MyTestObjectThatPretendsToBeAWidget new]];

ただし、クライアントがウィジェットについて何も知らない場合は、最初のパターンの方がクリーンです。

于 2013-03-08T13:48:46.510 に答える
0

1つのアプローチは、インスタンスメソッド-widgetClassをに追加することです。MySpecialController

@implementation MySpecialController

- (id)init
{
    self = [super init];
    if (self) {
        self.widget = [[[self widgetClass] alloc] init];
    }
    return self;
}

- (id)widgetClass
{
    return [MySpecialWidget class];
}

//...

@end

でそのメソッドをオーバーライドしますMyExtraSpecialController

@implementation MyExtraSpecialController

- (id)widgetClass
{
    return [MyExtraSpecialWidget class];
}

//...

@end
于 2013-03-08T14:06:16.853 に答える