Поставщик данных вызывает делегата: специфика или общая?
У меня есть синтаксический анализатор XML, который будет анализировать 17 различных документов XML (я упрощаю это). Когда анализатор завершил свою работу, он вызывает объект, который сделал запрос.
Первый путь
Единственный метод, который выглядит как
- (void)didReceiveObject:(NSObject *)object ofType:(MyObjectType)type
с MyObjectType, являющимся перечислением.
В этом методе я проверяю тип и перенаправляю объект в соответствующий метод.
Второй способ
Существует метод обратного вызова для каждого из 17 типов объектов, которые я могу получить.
- (void)didReceiveFoo:(MYFoo *)foo
- (void)didReceiveBar:(MYBar *)bar
... and so on
Какой способ использования делегатов будет лучше? Мы обсуждали это с коллегой и не могли найти один способ более привлекательный, чем другой. Кажется, что он просто решает, какой метод вызывать из парсера или внутри делегата....
Даже думая о добавлении будущих обратных вызовов методов / делегатов, мы не видим никаких реальных проблем.
Один из этих способов лучше, чем другой? Есть ли другой способ?
3 ответа
Первый метод:
Плюсы:
- Более гибкий к будущим изменениям.
Минусы:
- Может привести к большому выражению switch или кавычкам if... else if... else.
- Вероятно, в любом случае приводит к ряду явных методов.
- Требуется тип приведения.
Второй метод:
Плюсы:
- Нет типа литья.
- Если методы являются необязательными, делегат беспокоит только те объекты, которые его интересуют.
Минусы:
- Если методы не являются необязательными, а интерфейс раскрывается позже, все делегаты будут получать предупреждения до тех пор, пока не будут реализованы новые методы.
- Если методы не являются необязательными, это может быть много методов для реализации для каждого делегата.
Обычно при создании интерфейсов делегатов я склоняюсь к обобщению для будущей расширяемости. Изменение API, особенно с открытым исходным кодом, может быть очень сложным. Кроме того, я не совсем понимаю, почему у вас один парсер XML делает так много. Вы можете рассмотреть другой дизайн. 17 разных XML-документов кажутся много. Помимо этого, я предложу третий метод.
Третий метод:
Создайте словарь, который отображает строки в блоки. Блоки, вероятно, будут иметь тип void(^BlockName)(id obj)
, Ваш парсер определит серию строк, которые будут ключами для ваших различных блоков. Например,
NSString * const kFooKey = @"FooKey";
NSString * const kBarKey = @"BarKey";
// And so on...
Кто бы ни создавал синтаксический анализатор XML, он регистрирует блок для каждого ключа, который их интересует. Он должен регистрироваться только для ключей, которые ему интересны, и он полностью гибок для будущих изменений. Поскольку вы регистрируетесь для явных ключей / объектов, вы можете утверждать переданный тип без приведения типа (по сути, Design By Contract). Это может быть слишком сложно для того, что вы хотите, но я нашел подобные проекты очень полезными в моем коде. Он сочетает в себе плюсы обоих ваших решений. Это основной недостаток, если вы хотите использовать SDK, который не имеет блоков. Тем не менее, блоки становятся де-факто стандартом с Objective-C.
Кроме того, вы можете определить протокол, который включает в себя общие функциональные возможности ваших 17 объектов, если вы еще этого не сделали. Это изменит тип вашего блока на void(^BlockName)(id<YourProtocol> obj)
,
Почему бы не пойти с
- (void)didReceiveObject:(NSObject *)object
а потом проверять тип класса?
Это кажется более понятным и более расширяемым для меня, потому что это означает, что вы можете анализировать другие объекты в будущем без добавления дополнительных обратных вызовов.
(Я знаю, что это то же самое, что и первый вариант, но я хотел отметить, что ваш второй аргумент не нужен).
Вот решение.
Мы будем реализовывать оба варианта и посмотрим, какой из них больше используется.
Первый способ самый простой и быстрый, поэтому мы оставим его для внутренних нужд.
Но мы можем отправлять этот код как статическую библиотеку, поэтому мы хотим предоставить минимальный объем информации. Поэтому мы будем придерживаться второго способа.
Поскольку для каждого обратного вызова должен быть большой кусок кода, универсальным способом, безусловно, будет большой оператор switch, указанный rbrown.
Спасибо за помощь.