Observer
Un patrón de comportamiento: cuando un objeto cambia, los que dependen de él se enteran, sin que él tenga que conocerlos uno por uno.
También llamado: Listener
Observer es un patrón de comportamiento que define una dependencia de uno a muchos entre objetos, de forma que cuando uno cambia, todos los que dependen de él se enteran automáticamente.
El problema
Cuando un producto de AndesShop que estaba agotado vuelve a tener stock, varias cosas tienen que reaccionar: hay que avisarle a cada usuario que se anotó en la lista de “avisame cuando esté disponible”, hay que actualizar el índice de búsqueda para que el producto vuelva a aparecer en los resultados, y hay que refrescar la caché de recomendaciones. Si el código que actualiza el stock llama directamente a cada uno de esos tres procesos, cada vez que se agregue un cuarto proceso interesado en ese evento (por ejemplo, analytics) hay que volver a modificar esa misma función — que termina sabiendo demasiado sobre quién más necesita enterarse de un cambio de stock.
La solución
Observer invierte esa dependencia: el objeto que cambia (Product) no conoce a quienes están interesados en sus cambios. En cambio, mantiene una lista de “observadores” que se suscriben a sus eventos, y simplemente les avisa a todos cuando ocurre algo relevante. Sumar un interesado nuevo es agregarlo a la lista de suscriptores, sin tocar el código de Product.
classDiagram
class StockSubject {
<<abstract · subject>>
-observers List~StockObserver~
+subscribe(StockObserver) void
+unsubscribe(StockObserver) void
#notifyObservers() void
}
class Product {
-sku String
-stock int
+setStock(int) void
}
class StockObserver {
<<interface>>
+onBackInStock(Product) void
}
class WaitlistNotifier {
+onBackInStock(Product) void
}
class SearchIndexUpdater {
+onBackInStock(Product) void
}
StockSubject <|-- Product
StockSubject --> "0..*" StockObserver : observers
StockObserver <|.. WaitlistNotifier
StockObserver <|.. SearchIndexUpdaterEjemplo en Java
interface StockObserver {
void onBackInStock(Product product);
}
class Product {
private final List<StockObserver> observers = new ArrayList<>();
private int stock;
public void subscribe(StockObserver observer) {
observers.add(observer);
}
public void setStock(int newStock) {
boolean wasOutOfStock = this.stock == 0;
this.stock = newStock;
if (wasOutOfStock && newStock > 0) {
for (StockObserver observer : observers) {
observer.onBackInStock(this);
}
}
}
}
class WaitlistNotifier implements StockObserver {
public void onBackInStock(Product product) {
System.out.println("Notifying waitlist for " + product);
}
}
class SearchIndexUpdater implements StockObserver {
public void onBackInStock(Product product) {
System.out.println("Re-indexing " + product + " for search");
}
}
// Client code: sumar un interesado nuevo no toca la clase Product
Product jacket = new Product();
jacket.subscribe(new WaitlistNotifier());
jacket.subscribe(new SearchIndexUpdater());
jacket.setStock(10); // dispara ambas reacciones, sin que Product las conozca
Cuándo usarlo
- Cuando un cambio en un objeto necesita disparar reacciones en un número variable de otros objetos, que se conocen recién en tiempo de ejecución.
- Cuando querés evitar que el objeto que cambia dependa directamente de todos los que reaccionan a su cambio.
Cuándo evitarlo
Si el orden en que se notifica a los observadores importa y es rígido, o si necesitás garantías estrictas de que la notificación se procesó antes de continuar, un mecanismo de eventos simple puede no alcanzar — vale la pena evaluar una cola de mensajes o un mecanismo más explícito.
Ventajas y desventajas
| Ventajas | Desventajas |
|---|---|
| Desacopla al objeto que cambia de quienes reaccionan a ese cambio | El orden de notificación entre observadores no siempre es predecible ni controlable |
| Permite agregar o quitar observadores en tiempo de ejecución | Un observador que falla puede afectar a los demás si no se maneja con cuidado |
| Sigue el principio de abierto/cerrado: sumar un interesado nuevo no modifica al que cambia | Si hay muchos observadores encadenados entre sí, puede ser difícil rastrear el flujo completo de reacciones |
Relación con otros patrones
- Mediator también reduce dependencias entre objetos, pero centraliza toda la coordinación; Observer solo notifica eventos de uno a muchos, sin que el emisor decida cómo reaccionan.
- Es la base conceptual detrás de mecanismos de eventos y de programación reactiva más generales.
