Шаблон декоратора в Objective-C - PullRequest
9 голосов
/ 30 ноября 2010

Я думал об использовании шаблона декоратора для расширения функциональности класса 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 ]

4 голосов
/ 30 ноября 2010

Да, возможно реализовать шаблон Decorator в Objective-C с помощью NSProxy . Вам нужно будет реализовать methodSignatureForSelector: и forwardInvocation: для пересылки сообщений на оформленный объект.

Но я не думаю, что это было бы хорошим решением для того, что вы пытаетесь сделать здесь; отправка вызовов очень дорогостоящая и не предназначена для этой цели - безусловно, есть лучшие способы достичь того, что вы хотите сделать, например, с категориями (или, может быть, методом метания).

3 голосов
/ 30 ноября 2010

Ваш комментарий в интерфейсе:

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

неверно. Вам не нужно повторно объявлять методы, унаследованные от суперкласса в интерфейсе, даже если вы повторно их реализуете. Это не C ++.

Однако вам необходимо создать реализации всех методов, которые перенаправляют в представление прокрутки в переменной экземпляра. Может быть умный способ перехватить сообщения для экземпляров вашего подкласса и автоматически переслать их в переменную экземпляра, но я не могу думать, что это будет у меня из головы.

Как уже говорили другие, рассмотрите возможность использования категории UIScrollView. Категории не являются прямой заменой для декораторов, так как реализация категории даст всем экземплярам UIScrollView ваш собственный метод, но это, вероятно, не будет проблемой.

Обновление

Вы можете использовать -forwardingTargetForSelector: для перенаправления сообщений, которые вы не внедрили, в оформленный объект. Однако помните, что это будет медленнее, чем реализация каждого метода напрямую или использование категорий.

2 голосов
/ 30 ноября 2010

это не только проблема скучного кодирования метода, но и дублирование объекта, то есть будут созданы два объекта UIScrollView, декоратор и декорированный объект. Это может быть полезно иногда, когда вам действительно нужны два разных объекта и их данные, но один из них является основным, я называю это «дешевым декоратором». Но если сам по себе декоратор не нуждается и использует своих собственных членов, я называю это «грязным декоратором» (не беспокойтесь, вы не одиноки, и я также повторяю это несколько раз ;-) Чистый декоратор реализует тот же интерфейс ( Java) соотв. категория (Obj-C) похожа на декорированный объект.

Возможно, достаточно подкласса UIScrolView переписать те методы, которые вас интересуют. Или категории, подобные предложенному Мэтью.

Приветствия

Kay

2 голосов
/ 30 ноября 2010

Я верю, что вы хотите создать категорию. Категории позволяют добавлять дополнительные методы в существующий класс, в том числе в базовые классы.

Справочная библиотека iOS: категория

1 голос
/ 07 декабря 2010

Если все, что вы добавляете - это дополнительные методы, переходите с категориями. Просто импортируйте свой заголовок и вызовите метод в любом 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 голосов
/ 26 июля 2011

Я думаю, что это близко к шаблону декоратора:

ИСПОЛЬЗОВАНИЕ:

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

Таким образом tableView будет продолжать реагировать на события TableView и дополнительно на события ScrollView от _decorator

MENTION: _decorator может быть получен из любого класса, который реализует UIScrollViewDelegate

Я думаю, что это похоже на UINavigationController или UITabbarController, которые, на мой взгляд, очень близки к шаблону декоратора.Вероятно, вы не можете сделать это со всеми типами объектов, но это работает с типом вещей 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
...