Objective-C中的装饰模式

时间:2010-11-30 09:49:22

标签: iphone objective-c design-patterns decorator

我在考虑使用decorator pattern来扩展UIKit类的功能。我面临的问题是,从我在其他语言中看到的例子中,模式迫使我复制装饰对象的界面。以下是我将如何实现该模式:

// 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这样的东西吗?

编辑:这对我来说已经成为一个理论上的问题,因为我意识到装饰模式不是解决我实际问题所需要的。但是,由于我将来可能再次使用它,我仍然对你的答案非常感兴趣。

6 个答案:

答案 0 :(得分:4)

是的,可以使用NSProxy在Objective-C中实现Decorator模式。您必须实现methodSignatureForSelector:和forwardInvocation:到forward messages到装饰对象。

但我不认为这对你试图在这里做的事情是一个很好的解决方案;发送调用非常costly,并非用于此目的 - 当然有更好的方法来实现您想要做的事情,例如:与类别(或可能是方法调配)。

答案 1 :(得分:3)

您在界面中的评论:

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

不正确。即使您正在重新实现它们,也不必重新声明从接口中的超类继承的方法。这不是C ++。

但是,您必须创建重定向到实例变量中滚动视图的所有方法的实现。可能有一种聪明的方法可以拦截消息到你的子类的实例并自动将它们转发给实例变量,但是我无法想象它会是什么样的。

正如其他人所说,请考虑使用UIScrollView类别。类别不是装饰器的直接替代品,因为实现类别会为UIScrollView的所有实例提供自定义方法,但这可能不会成为问题。

<强>更新

您可以使用-forwardingTargetForSelector:将尚未实现的邮件重定向到装饰对象。但是,请注意,这比直接实现每个方法或使用类别要慢。

答案 2 :(得分:2)

我相信你想要的是创建一个类别。类别允许您向现有类添加其他方法,包括核心类。

iOS Reference Library: Category

答案 3 :(得分:2)

它不是(仅)一个无聊的方法编码的问题,而是对象的重复,即将创建两个UIScrollView对象,装饰器和装饰对象。当你真的需要两个不同的对象及其数据时,这可能是有用的,但其中一个是主人,我称之为“便宜的装饰者”。但是如果装饰者本身并不需要并使用自己的成员,我称之为“肮脏的装饰者”(不用担心,你不是一个人而且我几次也会这样做;-)纯装饰者将实现相同的界面( Java)resp。类别(Obj-C)就像装饰物一样。

也许将UIScrolView子类化为覆盖您感兴趣的那些方法就足够了。或者像Matthew建议的类别。

干杯

答案 4 :(得分: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

答案 5 :(得分:0)

我认为这接近装饰模式:

用法:

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

通过这种方式,tableView将继续响应它的TableView事件以及来自_decorator的ScrollView事件

提示:_decorator可以从任何实现UIScrollViewDelegate的类派生

我认为这类似于UINavigationController或UITabbarController,在我看来,它非常接近装饰模式。可能你不能用所有类型的对象做这个,但它确实适用于UIView类型的东西。还可以查看“Cocoa Desgn Patterns”一书,了解一些美味的Cocoa模式。不幸的是,我发现装饰器很少。

@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