Skip to content
StackPractices
beginner Por Mathias Paulenko

Patrón Observer

Define un mecanismo de suscripción para notificar a múltiples objetos sobre eventos. Patrón de diseño conductual para comunicación basada en eventos.

Temas: design

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Patrón Observer

Visión general

El Patrón Observer es un patrón de diseño conductual que define un mecanismo de suscripción para notificar a múltiples objetos sobre eventos que ocurren en el objeto que están observando. Establece una dependencia uno-a-muchos entre objetos.

Es la base de arquitecturas basadas en eventos, programación reactiva y la arquitectura Model-View en frameworks de UI.

Cuándo usarlo

Usa el Patrón Observer cuando:

  • Los cambios en un objeto requieren actualizar un número desconocido de objetos dependientes. Consulta Mediator Pattern para enrutamiento centralizado.
  • Necesitas un modelo de comunicación publicar-suscribir. Consulta CQRS Pattern para arquitecturas event-driven.
  • Un objeto debe notificar a otros sin saber quiénes son
  • Quieres acoplamiento débil entre productores y consumidores de eventos
  • Construyes componentes de UI reactivos o feeds de datos en tiempo real. Consulta API REST para fetching de datos en tiempo real.

Solución

Python

class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def notify(self, data):
        for observer in self._observers:
            observer.update(data)

class Observer:
    def update(self, data):
        print(f"Recibido: {data}")

# Uso
subject = Subject()
subject.attach(Observer())
subject.attach(Observer())
subject.notify("¡Hola observadores!")

JavaScript

class Subject {
  constructor() {
    this.observers = [];
  }

  subscribe(fn) {
    this.observers.push(fn);
  }

  notify(data) {
    this.observers.forEach((fn) => fn(data));
  }
}

// Uso
const subject = new Subject();
subject.subscribe((data) => console.log("A:", data));
subject.subscribe((data) => console.log("B:", data));
subject.notify("¡Hola observadores!");

Java

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String data);
}

class Subject {
    private final List<Observer> observers = new ArrayList<>();

    void attach(Observer o) {
        observers.add(o);
    }

    void notifyObservers(String data) {
        for (Observer o : observers) {
            o.update(data);
        }
    }
}

// Uso
Subject subject = new Subject();
subject.attach(data -> System.out.println("Recibido: " + data));
subject.notifyObservers("¡Hola observadores!");

Explicación

El Patrón Observer consiste en dos roles principales:

  • Subject (Publicador): Mantiene una lista de observadores y envía notificaciones
  • Observer (Suscriptor): Define una interfaz para objetos que deben ser notificados de cambios

Cuando el estado del Subject cambia, itera sobre sus observadores y llama su método update. Los observadores pueden suscribirse o darse de baja dinámicamente sin que el Subject conozca las clases concretas.

Variantes

VarianteCaso de usoCompromiso
Modelo pushEl subject envía datos completos a observadoresSimple, pero puede enviar datos innecesarios
Modelo pullEl subject notifica; los observadores consultan detallesMás eficiente, pero añade idas y vueltas
Event busUn despachador central desacopla subjects y observersMás flexible, añade indirección

Lo que funciona

  • Desuscríbete de observadores cuando se destruyen para prevenir fugas de memoria
  • Evita actualizaciones circulares donde observadores disparan cambios de vuelta al subject
  • Usa referencias débiles en lenguajes que lo soportan (ej. Java) para limpieza automática
  • Mantén la lógica de notificación simple y evita cómputos pesados en el loop de notify
  • Documenta los payloads de eventos para que los observadores sepan qué datos esperar

Errores comunes

  • Fugas de memoria: Olvidar desvincular observadores cuando ya no se necesitan
  • Orden de actualización inesperado: Los observadores pueden ejecutarse en orden indefinido; no dependas de ello
  • Bucles infinitos: Un observador que modifica el subject puede desencadenar actualizaciones en cascada
  • Acoplamiento fuerte: Dar a los observadores acceso al subject completo en lugar de solo los datos que necesitan
  • Bloqueo síncrono: Ejecutar observadores lentos en el hilo principal de notificación

Preguntas frecuentes

P: ¿Cuál es la diferencia entre Observer y Pub/Sub? R: Observer es una relación directa subject-observer. Pub/Sub añade un broker de eventos (Mediator) que desacopla completamente a los publicadores de los suscriptores.

P: ¿Sigue siendo relevante el Patrón Observer con frameworks reactivos modernos? R: Sí. React hooks, RxJS y el sistema de reactividad de Vue están construidos sobre conceptos de Observer. Para brokers de eventos singleton, consulta Singleton.

P: ¿Cómo evito fugas de memoria con observadores? R: Siempre proporciona un mecanismo de desuscripción y llámalo en manejadores de limpieza o destructores.

¿Es este patrón adecuado para proyectos pequeños?

Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.

¿Cómo se compara este patrón con alternativas?

Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.

¿Puedo aplicar este patrón parcialmente?

Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.

Temas Avanzados

Escenario: Sistema de Eventos con Observer Pattern

// Observer pattern para sistema de notificaciones
interface Observer {
  update(event: string, data: unknown): void;
}

class EventEmitter {
  private observers: Map<string, Set<Observer>> = new Map();

  subscribe(event: string, observer: Observer): void {
    if (!this.observers.has(event)) {
      this.observers.set(event, new Set());
    }
    this.observers.get(event)!.add(observer);
  }

  unsubscribe(event: string, observer: Observer): void {
    this.observers.get(event)?.delete(observer);
  }

  emit(event: string, data: unknown): void {
    this.observers.get(event)?.forEach(obs => {
      try {
        obs.update(event, data);
      } catch (err) {
        console.error(`Observer error: ${err}`);
      }
    });
  }
}

// Uso: sistema de e-commerce
const emitter = new EventEmitter();

// Observers
class EmailNotifier implements Observer {
  update(event: string, data: unknown): void {
    if (event === "order.created") {
      sendEmail((data as Order).userEmail, "Order confirmed");
    }
  }
}

class InventoryUpdater implements Observer {
  update(event: string, data: unknown): void {
    if (event === "order.created") {
      decrementStock((data as Order).items);
    }
  }
}

class AnalyticsTracker implements Observer {
  update(event: string, data: unknown): void {
    trackEvent(event, data);
  }
}

// Subscribe
emitter.subscribe("order.created", new EmailNotifier());
emitter.subscribe("order.created", new InventoryUpdater());
emitter.subscribe("order.created", new AnalyticsTracker());

// Emit
emitter.emit("order.created", { id: "123", userEmail: "user@example.com", items: [...] });
// Resultado: email enviado, stock actualizado, analytics tracked

Lecciones:

  • Observer desacopla el emisor de los receptores
  • Cada observer es independiente: fallo en uno no afecta otros
  • Usar Set para evitar duplicados
  • Try-catch en emit: un observer roto no rompe el evento
  • Para sistemas distribuidos, usar message broker (RabbitMQ, Kafka)
  • Memory leak: unsubscribe cuando el observer ya no es necesario

### Como evito memory leaks con observers?

Siempre llama unsubscribe cuando el observer ya no es necesario. En React, usa useEffect cleanup: subscribe en mount, unsubscribe en unmount. En Node.js, usa WeakRef o limpia explicitamente en shutdown. Si observers crecen sin limite, usa un Map con TTL o un max de observers por evento.


























End of document. Review and update quarterly.