design-patterns - observer - command pattern java
Callback/Command vs EventListener/Observer Pattern (4)
Ambos patrones comparten pocas intenciones comunes, excepto
El patrón observable puede notificar a múltiples oyentes. Por otro lado, el patrón de comando será el más adecuado, donde necesita un solo controlador de devolución de llamada.
En el patrón de comando, es fácil implementar la operación de deshacer.
¡Aclamaciones!
Estoy tratando de diseñar un marco asíncrono y quería saber lo que la gente piensa que son los pros / contras del patrón de devolución de llamada frente al patrón del observador.
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 usar ambos 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 mediante la creación de 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, por ejemplo generando un nuevo hilo para cada oyente frente a las notificaciones seriales. (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 deberías usar cada uno?
Thxs.
Ambos patrones son geniales y uno a elegir depende de lo que va a construir y cómo se utilizará su marco.
Si está intentando construir algún tipo de sistema de publicación y suscripción con el siguiente flujo de trabajo típico:
- el cliente inicia una tarea asincrónica y se olvida de ella
- múltiples manejadores reciben notificaciones cuando se completa la tarea
entonces el patrón Observer
es una elección natural para ti. Como está haciendo un marco, también debería considerar usar el patrón EventBus para lograr un acoplamiento flexible.
Si no necesita nada más que una simple ejecución asincrónica y un uso de flujo típico de su marco es:
- comenzar la tarea asincrónica
- hacer algo cuando se complete
o
- comenzar la tarea asincrónica
- hacer algo
- espere hasta que se complete y haga algo
entonces deberías ir con Callback
simple.
Pero para lograr una API más útil y limpia, le recomendaría deshacerse de la abstracción de Callback
de Callback
y diseñar su código de trabajador para devolverle algún tipo de Future
.
public interface Worker<T> {
Future<T> doAsync();
}
Y Worker
se puede usar de la 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 futuro estándar de Java . Pero te sugiero que uses ListenableFuture
de la biblioteca de guayaba.
Los patrones de comando, devolución de llamada y observador tienen una semántica diferente:
- devolución de llamada : notifica a un único interlocutor que alguna operación finalizó con algún resultado
- observador : notifica a cero las partes interesadas que ocurrió algún evento (por ejemplo, una operación finalizada)
- comando - encapsula una llamada de operación en un objeto, haciéndolo así transferible a través de un cable o persistible
En su ejemplo, puede combinar patrones de devolución de llamada y de observador para lograr una mayor flexibilidad API:
- Utilice el patrón de devolución de llamada para desencadenar operaciones y notifique a la persona que llama de forma asíncrona que finalizó la operación desencadenada.
- Utilice el patrón de evento / observador para dar a otros componentes (que no desencadenaron la operación) la oportunidad de recibir una notificación cuando finaliza una operación.
Yo dirí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 funcionamiento sería la forma en que GWT maneja la comunicación entre el navegador y el servidor .
Es posible que desee utilizar 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);
}
}