Должен ли мой API-интерфейс на основе блоков просто завершить работу или как обработчики успеха, так и неудачи?

При разработке блочного API в ObjC, какой подход лучше, один блок завершения или два, по одному для успеха и отказа?

Скажем, у нас есть метод, который асинхронно возвращает что-либо в блок, с одним блоком завершения он будет:

- (void) retrieveSomethingCompletion:(void (^)(id retrievedObject, NSError *error))completionBlock;

И с блоками успеха/отказа (стиль AFNetworking):

- (void) retrieveSomethingSuccess:(void(^)(id retrievedObject))successBlock failure:(void(^)(NSError *error))failureBlock;

Я всегда использую второй подход, но каковы преимущества/недостатки каждого варианта? Что вы обычно используете и почему?

Ответ 1

Оба являются прекрасными образцами (и я проголосовал за то, что оба Фиро и Джон отвечают, потому что - да, это вопрос вкуса, и оба их ответа находятся на месте), но есть несколько отличных преимуществ для перехода с одним блоком, по моему опыту. Джон Вудс поднимает превосходный момент в отношении "аромата" API, хотя я бы утверждал, что сетевая операция всегда завершается (если у нее нет тайм-аута, который является другим классом ошибок) и что способ успеха/отказа не является вполне либо/или.

  • Это устраняет необходимость дублирования кода между блоками, которые являются общими для разрыва задачи при завершении независимо от успеха или сбоя.

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

  • В некоторых ситуациях отказ может фактически генерировать данные, которые должны обрабатываться таким образом, как путь успеха. В меньшем количестве случаев успешное завершение может фактически нести ошибку. Хотя шаблон NSError** на методах является чисто или/или, одно из преимуществ использования либо шаблона блока заключается в том, что это можно выразить. Дополнительным преимуществом использования одного блока завершения является то, что вы избегаете дублирования логики.

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

Это позволяет избежать бессмыслицы:

 [foo doSomething: ^{
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
  } andSomethingElse: ^{
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
       .... lots of code ... 
   }];

Ответ 2

Обычно я использую вторую реализацию. Несколько причин для этого:

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

  • Мне нравится избегать ненужных проверок, когда это возможно ( "если ошибка тогда что-то делает с моей ошибкой, иначе что-то с моим объектом" ). Поскольку я либо получу объект, либо ошибку (никогда не оба), я считаю неудобным передавать оба параметра, когда только один будет действительным.

  • Я использую Restkit, и мне просто нужно передать блоки в мои вызовы API, и он будет автоматически вызывать блок успеха или отказа, когда приходит вызов. Если я выполняю вторую реализацию, мне нужно обработать обратные вызовы метода (отказ или метод успеха/блок) и выполнить блок вручную. Однако я не уверен в AFNetworking.

Я лично считаю, что оба варианта прекрасны, но в этой ситуации я буду спорить о втором.

Ответ 3

По-моему, это очень субъективно. Размышляя о сценариях использования для вашего API, пользователь выиграет больше от завершения или успеха/неудачи?

Если его что-то вроде API доступа к сети ala AFNetworking, вероятно, идет с успехом/неудачей - его более уместно. Однако, если API, вероятно, успешно завершит работу каждый раз, когда блок анимации будет выполнен, то лучше всего