El patrón Observer: el átomo conceptual de la reactividad
El patrón Observer de la Gang of Four define una dependencia uno-a-muchos: un sujeto observable guarda una lista de observadores y los notifica automáticamente cuando su estado cambia. Es la semilla de la que brotan signals, observables y stores; entender su anatomía y sus grietas es entender por qué existe todo lo demás del track.
Antes de que existieran los signals, los observables o Redux, existía el patrón Observer: un sujeto que guarda una lista de dependientes y los avisa cuando cambia. La Gang of Four lo catalogó en 1994, pero su linaje llega hasta el MVC de Smalltalk-80. Todo mecanismo de reactividad que usarás en 2026 es, en el fondo, una elaboración sofisticada de esta única idea: separar a quien cambia de quienes reaccionan.
- Entender la dependencia uno-a-muchos entre un sujeto y sus observadores.
- Distinguir los roles del patrón:
Subject,Observery el contratoupdate. - Implementar un sujeto observable mínimo con
subscribe,unsubscribeynotify. - Reconocer las grietas del patrón que motivan cada primitivo posterior del track.
El problema: acoplar a quien cambia con quienes reaccionan
Imagina un dato que muta —el precio de un carrito, la posición del cursor, el estado de una conexión— y varias partes del sistema que deben enterarse. La solución ingenua es que el dueño del dato llame directamente a cada interesado: actualizarCabecera, actualizarResumen, actualizarBadge. Funciona hasta que aparece el cuarto interesado, o hasta que el dato vive en una librería que no puede conocer a la interfaz. El dato queda acoplado a la lista concreta de sus consumidores, y esa lista crece sin control.
El patrón Observer invierte la relación. En lugar de que el sujeto conozca a cada consumidor por su nombre, define un contrato abstracto —“si te interesa, tráeme algo que sepa reaccionar”— y guarda una colección de esos reaccionadores. El sujeto ya no sabe quién lo escucha ni qué hace cada uno; solo sabe que, cuando cambie, debe recorrer su lista y avisar. Esa inversión —de llamada explícita a notificación abstracta— es la primera y más importante decisión de diseño de toda la reactividad.
Históricamente la idea nace en el MVC de Smalltalk-80: la Vista observa al Modelo y, cuando el Modelo cambia, se redibuja sola sin que el Modelo sepa que existen vistas. Esa separación entre estado y presentación sigue siendo el motivo por el que hoy escribimos interfaces declarativas.
Anatomía del patrón
El patrón tiene dos roles. El Subject (o sujeto observable) mantiene el estado y la lista de observadores, y expone alta, baja y una notificación interna. El Observer cumple un contrato mínimo: un método update que el sujeto invoca. Una implementación tipada en TypeScript:
interface Observer<T> {
update(value: T): void;
}
class Subject<T> {
#observers = new Set<Observer<T>>();
#value: T;
constructor(initial: T) {
this.#value = initial;
}
subscribe(observer: Observer<T>): void {
this.#observers.add(observer);
observer.update(this.#value); // entrega el valor actual al entrar
}
unsubscribe(observer: Observer<T>): void {
this.#observers.delete(observer);
}
set(value: T): void {
if (Object.is(value, this.#value)) return; // no notifiques si no cambió
this.#value = value;
this.#notify();
}
#notify(): void {
for (const observer of this.#observers) {
observer.update(this.#value);
}
}
}
Cada decisión importa. El Set garantiza que un mismo observador no se registre dos veces y da alta y baja en tiempo constante. La comparación con Object.is evita notificar cuando el valor no cambió de verdad —la primera defensa contra el trabajo redundante—. Y entregar el valor actual dentro de subscribe resuelve un problema sutil: quien llega tarde también quiere el estado presente, no solo los cambios futuros. Guarda esa distinción, porque en la próxima lección los event emitters la romperán.
Verlo funcionar
Con el sujeto en la mano, usarlo es trivial: creas observadores que cumplan el contrato update y los das de alta.
const temperatura = new Subject(20);
const consola: Observer<number> = { update: (t) => console.log("temp", t) };
const alarma: Observer<number> = { update: (t) => { if (t > 30) avisar(); } };
temperatura.subscribe(consola); // imprime 20 al entrar
temperatura.subscribe(alarma);
temperatura.set(35); // ambos reaccionan; la alarma salta
temperatura.unsubscribe(alarma);
Fíjate en que el sujeto no sabe nada de consolas ni de alarmas: solo conoce el contrato update. Podrías añadir un tercer observador que escriba en una base de datos sin tocar ni una línea de Subject. Esa extensibilidad —sumar reacciones sin modificar la fuente— es exactamente lo que compra la inversión de control.
flowchart LR O1[Observer A] -.->|subscribe| S[Subject observable] O2[Observer B] -.->|subscribe| S O3[Observer C] -.->|subscribe| S S -->|update| O1 S -->|update| O2 S -->|update| O3 style S fill:#89b4fa,color:#11111b style O1 fill:#a6e3a1,color:#11111b style O2 fill:#a6e3a1,color:#11111b style O3 fill:#a6e3a1,color:#11111b
Las grietas que lo definen
El Observer es poderoso precisamente por lo mínimo que es, pero esa minimalidad deja grietas que el resto del track existe para tapar. Son cuatro, y conviene nombrarlas porque cada una reaparecerá convertida en el tema de una lección entera.
El sujeto guarda una referencia fuerte a cada observador, así que uno que se olvida de darse de baja no puede ser recolectado por el GC mientras el sujeto viva. El update avisa de que algo cambió, pero no de qué, y el observador acaba releyendo de más. Nada promete el orden de notificación. Y si un update provoca otro set, disparas una notificación anidada que puede ver estados a medio construir.
Lapsed listener
El sujeto retiene una referencia fuerte a cada observador. Quien no se da de baja no muere: fuga de memoria garantizada, tema de la lección de suscripciones manuales.
Granularidad gruesa
El update dice que algo cambió, pero no qué. El observador relee de más porque no sabe qué parte se movió.
Orden indefinido
Nada promete en qué orden se notifica. Si un observador depende de que otro ya se actualizó, el bug es silencioso.
Reentrada
Un update que dispara otro set anida notificaciones y expone estados a medio construir: el germen de los glitches.
Ninguna de estas grietas invalida el patrón; lo sitúan. Cada una es la puerta de entrada a una lección posterior: el lapsed listener abre la de suscripciones manuales, la reentrada y el orden abren la de push vs pull, y la granularidad gruesa es justo lo que el seguimiento fino de los signals viene a corregir. El Observer no es un patrón defectuoso, es un patrón mínimo, y su minimalidad es a la vez su fuerza pedagógica y la lista de tareas del resto del track.
El vocabulario que vas a reencontrar
Nada de esto es un ejercicio de museo. El esqueleto que acabas de escribir reaparece, con otros nombres, en cada herramienta del ecosistema de 2026. Reconocer la correspondencia es lo que te permite leer una librería nueva como una variación y no como un misterio.
| Rol del Observer | Su reencarnación moderna |
|---|---|
Subject que guarda el valor |
signal, ref, store de Redux o Zustand |
Observer.update |
efecto, watch, callback suscriptor |
subscribe |
leer un signal dentro de un efecto |
notify |
invalidación y recomputación del grafo |
| baja manual | limpieza automática de dependencias |
Las dos últimas filas encierran la trama del nivel: el Observer clásico te obliga a cablear y a limpiar a mano; la reactividad automática que verás al final del track hace ambas cosas por ti. Pero para apreciar esa magia hay que haber sufrido primero el cableado manual.
Cuando mires con calma cualquier sistema reactivo moderno, verás el mismo esqueleto que acabas de programar. Un signal es un Subject con lectura perezosa y seguimiento automático de dependencias. Un observable de RxJS es un Subject con un protocolo de tres mensajes —siguiente, error, completado— y decenas de operadores para transformarlo. Un store de Redux es un Subject con una única función pura que decide cómo muta el valor. Un EventTarget del navegador es un Subject que usa nombres de evento como canales. Ninguno inventa la reactividad: todos elaboran sobre la dependencia uno-a-muchos que la Gang of Four catalogó, añadiendo disciplina donde el patrón crudo dejaba grietas —memoria, granularidad, orden, composición—. Por eso este es el primer primitivo del nivel y no un tema más entre otros: quien entiende de verdad subscribe y notify no aprende cada librería desde cero, sino que la lee como una variación sobre un tema que ya domina. El resto del track no te enseñará reactividades distintas; te enseñará mejores formas de administrar esta misma. Interiorizar el átomo es dejar de coleccionar APIs para empezar a ver el patrón que todas comparten.
- Implementa
Subject<T>yObserver<T>como arriba, consubscribe,unsubscribeyset. - Registra dos observadores que impriman el valor y comprueba que ambos reciben cada cambio.
- Da de baja a uno con
unsubscribey verifica que deja de recibir notificaciones mientras el otro sigue. - Provoca una reentrada: haz que un observador llame a
setdentro de suupdatey observa qué ocurre. ¿Termina la cascada? ¿Qué valor ve cada observador?