2010-11-30 10 views
9

私はdecorator patternを使ってUIKitクラスの機能を拡張しようと考えていました。私が直面している問題は、私が他の言語で見た例から、パターンが私に装飾されたオブジェクトのインタフェースを複製させることです。ここで私は、パターン実装見るであろう方法は次のとおりです。だから、要約するObjective-Cのデコレータパターン

// Inheritance used for compile time check only 
@interface UIScrollViewDecorator : UIScrollView 
{ 
    UIScrollview *decoratedScrollView; 
} 

- (id)initWithScrollView:(UISCrollView*)scrollView; 

@end 

@implementation UIScrollViewDecorator 

- (id)initWithScrollView:(UISCrollView*)scrollView 
{ 
    self = [super init]; 
    if(self != nil) 
    { 
     decoratedScrollView = [scrollView retain]; 
     // maybe set up some custom controls on decoratedScrollView 
    } 
} 

// all methods for UIScrollView need to be manually passed to the decorated object 
// -- this is the problem 
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated 
{ 
    // use "overwritten methods" to mess with the input for the decorated scroll view 
    // most of the time though I have no need to make any adjustments here; I still need 
    // to pass all these messages through so that outside users can interact with me just 
    // like with a real UIScrollView 
    [decoratedScrollView scrollRectToVisible:rect animated:animated]; 
} 

@end 

を、問題は、私はそこに何かを変更する必要がない場合でも、飾られたオブジェクトのメソッドの重複です。私が "上書き"する必要がないメソッドに沿って渡す簡単な方法はありますか?これにNSProxyのようなものを使用できますか?

EDIT:私の実際の問題を解決するためにデコレータパターンが必要ではないことがわかって以来、これは主に理論上の問題になっています。しかし、将来私は再びそれを使うようになるかもしれないので、あなたの答えにはまだ非常に興味があります。

答えて

4

はい、NSProxyでObjective-CのDecoratorパターンを実装することは可能です。修飾されたオブジェクトにmethodSignatureForSelector:およびforwardInvocation:をforward messagesに実装する必要があります。

しかし、これはあなたがここでやろうとしていることに対しては良い解決策になるとは思わない。呼び出しを送信するのは非常にcostlyなので、そのような目的のためのものではありません。カテゴリ(または方法swizzling)。

+0

私は、スウィージングがObjective-Cでデコレータを行う適切な方法だと思います。 –

2

私はあなたが望むものはカテゴリを作成することだと考えています。カテゴリを使用すると、コアクラスを含む既存のクラスにメソッドを追加できます。

iOS Reference Library: Category

+0

。それは私の最初の考えでした。 Objective Cのカテゴリは非常に便利なものです。 – drekka

+1

カテゴリはメソッドを追加するのには適していますが、上書きされたメソッドの機能を拡張するためには使用できません。したがって、彼らはデコレータパターンの代用品ではありません。私は、デコレーションされたオブジェクトに新しいメソッドを追加するために使用してはならないので、パターンを誤解していることに気付きました。コード例を編集します。 –

2

それは退屈な方法コーディングのが、オブジェクトの重複の問題、すなわち2つのUIScrollViewのオブジェクト、デコレータと内装のオブジェクトが作成される(のみ)ではありません。これは実際には2つの異なるオブジェクトとそのデータが本当に必要なときに役立つかもしれませんが、そのうちの1つはマスターです。私はこれを「格安デコレータ」と呼びます。しかし、デコレータそのものが自分のメンバを必要とせず、使用していない場合、私はそれを「汚れたデコレータ」と呼んでいます(心配もなく、あなたもそれだけではありません。 Java)resp。カテゴリ(Obj-C)を指定します。

UIScrolViewをサブクラス化すれば、興味のあるメソッドを上書きするだけで十分です。またはMatthewのようなカテゴリが示唆しているかもしれません。

乾杯

ケイ

3

インタフェースのコメント:

// all methods of UIScrollView need to be duplicated here -- this is the problem 

が正しくありません。インターフェースを再実装していても、インターフェースのスーパークラスから継承したメソッドを再宣言する必要はありません。これはC++ではありません。

ただし、インスタンス変数のスクロールビューにリダイレクトするすべてのメソッドの実装を作成する必要があります。あなたのサブクラスのインスタンスへのメッセージを傍受し、それを自動的にインスタンス変数に転送する巧妙な方法があるかもしれませんが、それが私の頭の上から外れることは考えられません。

他の人が言っているように、代わりにUIScrollViewのカテゴリを使用することを検討してください。カテゴリを実装するとUIScrollViewのすべてのインスタンスにカスタムメソッドが渡されるため、カテゴリはデコレータの直接の置き換えではありませんが、おそらく問題にはなりません。

更新

あなたが装飾されたオブジェクトに実装していないメッセージをリダイレクトする-forwardingTargetForSelector:を使用することができます。ただし、各メソッドを直接実装するか、カテゴリを使用するよりも時間がかかることに注意してください。

+0

あなたは正しいです。私はコードを更新しました。もちろん、問題は同じままです。 –

+0

元のクラスのメソッドをオーバーライドする必要がある場合、カテゴリは実際には機能しません。主に、必要に応じて元の実装を呼び出すことができないためです。 [ここ](https://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html)を参照してください。私は 'forwardingTargetForSelector:'があなたの望むものだと思います。 – bcattle

1

あなたが追加されているすべての余分な方法はちょうどあなたのヘッダをインポートし、あなたが本当にそれをサブクラス化したい場合でも、あなたがこのよう

それを行うだろう任意のUIScrollViewの

@interface UIScrollView (Additions) 
-(void)doFancyStuff; 
@end 

@implementation UIScrollView (Additions) 
-(void)doFancyStuff 
{ 
    //your code 
} 
@end 

上のメソッドを呼び出す カテゴリで行く場合には

@interface MyCustomScrollVoew : UIScrollView 
{ 
} 
- (id)init; 
@end 

@implementation MyCustomScrollVoew 
- (id)init 
{ 
    if (self = [super init]) 
    { 
      //do some fancy stuff here 
    } 
    return self; 
} 

- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated 
{ 
    [super scrollRectToVisible:rect animated:animated]; 
    //do some extra stuff here 
    //by removing the first line you can simply override the method 
} 
@end 
0

私は、これはDecoratorパターンに近づくと思う:

USAGE:

_decoratorは、私はこの考えてUIScrollViewDelegate

を実装する任意のクラスから派生することができますのtableViewはそれが_decoratorからテーブルビューイベント、さらにScrollViewイベント

言及うに応えていきます。このように

DecoratorSample* _decorator = [[DecoratorSample alloc] init]; 
_decorator.scrollView = self.tableView; // or any other class derived from UIScrollView 
[_decorator.scrollView addSubview:_decorator]; 
[_decorator release]; 

UINavigationControllerまたはUITabbarControllerと似ていますが、私の意見では、デコレータのパターンに非常に近くなります。おそらく、すべてのタイプのオブジェクトでこれを行うことはできませんが、UIViewタイプのもので動作します。また、「ココアDesgn Patterns」という本を見て、おいしいココアパターンを確認してください。残念ながら、私がデコレータに関して見つけたものはほとんどありません。

@interface DecoratorSample : UIScrollView <UIScrollViewDelegate> 
{ 
    UIScrollView* _scrollView; 
    id<UIScrollViewDelegate> _forwardDelegate;  
} 

@property (nonatomic,assign) id<UIScrollViewDelegate> forwardDelegate; 
@property (nonatomic,retain) UIScrollView* scrollView; 

@end 

とこの第二

@implementation DecoratorSample 

@synthesize forwardDelegate = _forwardDelegate; 

- (void)dealloc 
{ 
    [_scrollView release]; 
} 

- (void) setScrollView:(UIScrollView*)scrollView 
{ 
    [_scrollView release]; 
    _scrollView = scrollView; 
    [_scrollView retain]; 

    _forwardDelegate = _scrollView.delegate; 
    _scrollView.delegate = self; 
} 

- (UIScrollView*) scrollView 
{ 
    return _scrollView; 
} 

#pragma UIScrollView implementation 

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
{ 
    if (_forwardDelegate != nil && [_forwardDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) 
    { 
     [_forwardDelegate scrollViewWillBeginDragging:scrollView]; 
    } 
//do something else 
} 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{ 
    if (_forwardDelegate != nil && [_forwardDelegate respondsToSelector:@selector (scrollViewDidScroll:)]) 
    { 
     [_forwardDelegate scrollViewDidScroll:scrollView]; 
    } 
    //do something else 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 
{ 
    if (_forwardDelegate != nil && [_forwardDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) 
    { 
     [_forwardDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 
    } 
//do something else 
} 

@end 
関連する問題