代码之家  ›  专栏  ›  技术社区  ›  Mihai Damian

目标C中的装饰图案

  •  9
  • Mihai Damian  · 技术社区  · 14 年前

    我想用 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 回复  |  直到 12 年前
        1
  •  4
  •   w-m    14 年前

    是的,可以使用 NSProxy . 您必须实现methodSignatureForSelector:和ForwardInvocation:到 forward messages 装饰物。

    但我不认为这是一个很好的解决方案,你在这里做什么,发送呼叫是非常 costly 并不是为了这样一个目的——当然有更好的方法来实现你想要做的事情,例如使用类别(或者方法旋转)。

        2
  •  3
  •   JeremyP    14 年前

    您在界面上的评论:

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

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

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

    正如其他人所说,考虑改用uiscrollview类别。类别并不是装饰器的直接替代,因为实现一个类别会给uiscrollview的所有实例您的自定义方法,但这可能不是问题。

    更新

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

        3
  •  2
  •   Matthew Frederick    14 年前

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

    iOS Reference Library: Category

        4
  •  2
  •   Kay    14 年前

    这不仅是一个无聊的方法编码问题,而且是对象的复制问题,即将创建两个uiscrollview对象,装饰器和装饰对象。有时,当您真正需要两个不同的对象及其数据时,这可能很有用,但其中一个是主对象,我称之为“廉价的装饰器”。但是,如果装饰器本身不需要和使用它自己的成员,我称之为“脏装饰器”(不用担心,您不是单独的,我也会这样做几次;-)纯粹的装饰器将实现相同的接口(Java)RESP。类别(obj-c)与装饰对象相似。

    也许这就足够子类uiscrolview覆盖您感兴趣的那些方法了。或者像马修建议的分类。

    干杯

        5
  •  1
  •   aryaxt    14 年前

    如果你要添加的只是额外的方法,那就用类别吧。 只需导入标题并调用任何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
    
        6
  •  0
  •   WalterF    13 年前

    我认为这接近装饰图案:

    用途:

    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事件。

    注意:修饰符可以从实现uiscrollViewDelegate的任何类派生

    我认为这类似于uinavigationcontroller或uitababarcontroller,在我看来,它非常接近于装饰器模式。可能您不能对所有类型的对象都这样做,但它确实适用于uiview类型的东西。还可以查看“可可设计图案”这本书,了解一些美味的可可图案。不幸的是,我发现关于装饰师的东西很少。

    @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