Обратный вызов / команда vs EventListener / шаблон наблюдателя

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

Callback pattern:

//example callback
public interface Callback{
    public void notify(MethodResult result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback callback){
     //do work
     callback.notify(result);
  }
}

//example observer pattern
public interface EventListener{
   public void notify(MethodResult result);

}

public class Worker{
  private EventListener listener;
  public registerEventListener(EventListener listener){
   this.listener=listener;
  }
  public void doAsyncWork(){
     //do work
     listener.notify(result);
  }
}

Я работаю с фреймворком, который, похоже, использует оба этих шаблона. Шаблон EventListener не является типичным шаблоном, поскольку у него нет списка слушателей. Это может быть легко реализовано путем создания CompositeListener, который имеет свою собственную семантику на приоритет слушателей и как обрабатывать распределение событий для каждого слушателя, например, создание нового потока для каждого слушателя и последовательных уведомлений. (Я действительно думаю, что это хорошая идея, поскольку это хорошее разделение проблем и улучшение стандартного шаблона наблюдателя/слушателя).

любые мысли о том, когда следует использовать каждый?

Thxs.

4 ответов


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

если вы пытаетесь построить какую-то систему публикации-подписки со следующим типичным потоком работы:

  • клиент запускает асинхронную задачу и забывает об этом
  • несколько обработчиков получает уведомления, когда задача выполнена

затем Observer массив-это естественный выбор для вас. Как и ты делаете фреймворк, который вы также должны рассмотреть с помощью EventBus картина для того чтобы достигнуть свободного соединения.

Если вам не нужно ничего больше, чем простое асинхронное выполнение и типичное использование потока вашей структуры:

  • пуск асинхронных задач
  • сделайте что-нибудь, когда он будет завершен

или

  • пуск асинхронных задач
  • сделать что-то
  • подождите, пока он не будет завершен и сделать что-то!--15-->

тогда вы должны пойти с простым Callback.

но для достижения более удобного и чистого API я бы рекомендовал вам избавиться от Callback абстракция и дизайн вашего рабочего кода, чтобы вернуть какой-то Future.

public interface Worker<T> {

    Future<T> doAsync();

}

и Worker можно использовать следующим образом:

Future<Integer> future = worker.doAsync();

// some work here

Integer result = future.get(); // waits till async work is done

Future может быть стандартный будущее java. Но я бы посоветовал вам использовать ListenableFuture из гуавы библиотека.


Шаблоны команд, обратного вызова и наблюдателя имеют различную семантику:

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

в вашем примере вы можете объединить шаблоны обратного вызова и наблюдателя для достижения большей гибкости API:

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

Я бы сказал, что шаблон обратного вызова лучше, поскольку он проще, что означает, что он будет более предсказуемым и с меньшей вероятностью будет иметь ошибки из-за собственного мутирующего состояния. Примером этого в работе будет способ GWT обрабатывает связь браузера / сервера.

вы можете использовать дженерики, хотя:

//example callback
public interface Callback<T> {
    public void notify(T result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback<SomeTypeOrOther> callback){
     //do work
     callback.notify(result);
  }
}

оба шаблона имеют мало общих намерений, за исключением,

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

в шаблоне команды легко реализовать операцию отмены.

Ура!