28

Estoy tratando de diseñar un marco asíncrono y quería saber qué piensan las personas que son los pros/contras del patrón de devolución de llamada frente al patrón del observador.Callback/Command vs EventListener/Observer Pattern

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); 
    } 
} 

Estoy trabajando con un marco que parece utilizar estos dos patrones. El patrón EventListener no es el patrón típico, ya que no tiene una lista de oyentes. Esto puede implementarse fácilmente creando un CompositeListener que tenga su propia semántica sobre la prioridad de los oyentes y cómo manejar la distribución de eventos a cada oyente, p. generando un nuevo hilo para cada oyente vs notificaciones en serie. (De hecho, creo que esta es una buena idea ya que es una buena separación de preocupaciones y es una mejora en el patrón estándar de observador/oyente).

¿Alguna idea de cuándo debe usar cada una?

Thxs.

+0

Para simplificar la devolución de llamada, le recomiendo que utilice las funciones de primera clase. así que es solo 'public void doAsyncWork (Función de devolución de llamada)' – Raynos

+0

Soy nuevo en ambos patrones de diseño, y me confundí mucho con los ejemplos de código. ¿Estás seguro de que estos son los modelos correctos de estos dos patrones, especialmente el patrón Observer? Para mí, la forma en que los escribiste no hace diferencia. No me malinterpretes sin embargo. Agradezco sinceramente su pregunta ya que tengo lo mismo en mente. Solo quiero decirlo. ¡Gracias! – jiu9x9uij

Respuesta

22

Ambos patrones son grandes y cuál elegir depende de lo que se va a construir y cómo se utilizará su marco.

Si usted está tratando de construir algún tipo de publicación-suscripción sistema con siguiente flujo típico de trabajo:

  • cliente se inicia la tarea asíncrona y se olvida de que
  • varios controladores recibe notificaciones cuando la tarea se ha completado

luego Observer patrón es una elección natural para usted. Como está haciendo un marco, también debería considerar usar el patrón EventBus para lograr un acoplamiento flojo.

Si necesita nada más que una simple ejecución asíncrona y un flujo típico que usa de su marco es:

  • tarea asíncrona inicio
  • hacer algo cuando se haya completado

o

  • iniciar tarea asíncrona
  • hacer algo
  • esperar hasta que se complete y hacer algo

, entonces debería ir con una simple Callback.

Pero con el fin de lograr la API más fácil de usar y limpia me gustaría recomendar que para deshacerse de Callback abstracción y el diseño de su código de trabajador para devolver una cierta clase de Future.

public interface Worker<T> { 

    Future<T> doAsync(); 

} 

Y Worker puede usarse siguiente manera:

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

// some work here 

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

Future podría ser un estándar java Future. Pero le sugiero que use ListenableFuture de la biblioteca de guayaba.

5

Argumentaría que el patrón de devolución de llamada es mejor, ya que es más simple lo que significa que será más predecible y menos propenso a tener errores debido a su propio estado de mutación. Un ejemplo de esto en operación sería the way GWT handles browser/server communication.

Es posible que desee utilizar los genéricos sin embargo:

//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); 
    } 
} 
25

de comandos, los patrones de devolución de llamada y de observación tienen diferentes semántica:

  • de devolución de llamada - Notifica a una sola persona que llama que alguna operación terminó con un resultado
  • observador - notifica a cero a partes n interesados ​​que algunos evento (por ejemplo, una operación finalizada)
  • comando - encapsula una llamada de operación en un objeto haciéndolo así transferible sobre un alambre o persisten-poder

En su ejemplo se podría combinar ambos patrones de devolución de llamada y de observación para lograr una mayor flexibilidad de la API:

  1. Usar el patrón devolución de llamada para activar las operaciones y notificar a la persona que llama de forma asincrónica que la operación disparada terminó.
  2. Use evento/observador patrón para dar algunos otros componentes (que no activan la operación) la posibilidad de recibir una notificación cuando finaliza una operación.
+1

El punto clave aquí es: - utilizar la devolución de llamada para notificar al desencadenante - utilizar eventos de fuego para notificar a los observadores, que en este caso, no hubieran desencadenado el evento. – metakungfu

+1

La explicación de las diferencias entre tres patrones me es útil. – Sentimental