Sebastian Gomez
Entendiendo el patrón observador (Observer Pattern) en JavaScript
En este post aprenderemos cómo implementar el patrón observer en JavaScript y en qué situaciones lo podemos usar.
El patrón observador es uno de los patrones de diseño de software más usados en JavaScript. De él se extienden aplicaciones importantes que pueden ayudarnos a definir mejores arquitecturas en aplicaciones web, por lo que su uso y estudio es altamente recomendado.
Patrón observador: conceptos clave
El patrón observador se define como un patrón de comportamiento en la programación orientada a objetos, y es responsable de la comunicación entre objetos.
Puedes encontrarlo también como patrón publicador suscriptor o pub/sub, un nombre que ya nos da una idea básica de lo que hace.
En términos simples, este patrón permite la notificación a objetos (los suscriptores u observadores) cuando cambia el estado de otro objeto (el publicador).
Implementación en JavaScript
Para implementar el patrón observador en JavaScript, comenzamos creando una clase llamada Publicador que contenga los métodos subscribe(), unsubscribe() y notify(). Aquí tienes el código de ejemplo:
// Define la clase "Publicador"
class Publicador {
// Constructor de la clase "Publicador"
constructor() {
// Crea una propiedad "subscriptors" para almacenar la lista de suscriptores
this.subscriptors = [];
}
// Método "subscribe" para agregar un nuevo suscriptor a la lista
subscribe(subscriptor) {
// Agrega el "subscriptor" pasado como argumento al final de la lista
this.subscriptors.push(subscriptor);
}
// Método "unsubscribe" para eliminar un suscriptor de la lista
unsubscribe(subscriptor) {
// Filtra la lista, manteniendo solo los que no coincidan con el argumento
this.subscriptors = this.subscriptors.filter(
(item) => item !== subscriptor
);
}
// Método "notify" para notificar un evento a todos los suscriptores
notify(event) {
// Itera sobre cada elemento de la lista de suscriptores
this.subscriptors.forEach((item) => {
// Llama a cada suscriptor pasando "this" como contexto y "event" como argumento
item.call(this, event);
});
}
}Ahora imaginemos que usaremos esta definición de Publicador para un periódico que regularmente publica nuevas ediciones.
const periodico = new Publicador();Pensemos en los clientes (los suscriptores), que necesitan saber cuándo llega una nueva edición del periódico. Inicialmente, los clientes son simples funciones:
// Define la función "Observer" que acepta un argumento "edicion"
function Observer(edicion) {
// Imprime un mensaje informando que llegó una nueva edición
console.log("Llegó una nueva edición con el nombre de: " + edicion);
}
// Usa "subscribe" del objeto "periodico" para suscribir la función "Observer"
periodico.subscribe(Observer);
// La suscribimos de nuevo: "Observer" será notificado dos veces por cada evento
periodico.subscribe(Observer);
// Notifica a todos los suscriptores (las dos instancias de "Observer")
// pasando como argumento el string "Nueva edicion"
periodico.notify("Nueva edicion");Si ejecutas este código en la consola del navegador o en Node.js, verás:
Llegó una nueva edición con el nombre de: Nueva edicion
Llegó una nueva edición con el nombre de: Nueva edicionComo un mejor acercamiento, podríamos definir una clase para los clientes que nos permita crear instancias y tener un control más granular. Además, definimos un método específico (buzon) para escuchar las nuevas ediciones del periódico:
// Define la clase "Subscriptor"
class Subscriptor {
// Constructor que acepta un argumento "id"
constructor(id) {
// Asigna el "id" a la propiedad del objeto
this.id = id;
// Informa que se ha creado un suscriptor con ese "id"
console.log("Se ha creado el suscriptor #: " + id);
}
// Método "buzon" que acepta un argumento "edicion"
buzon(edicion) {
// Informa que este suscriptor recibió una nueva edición
console.log(
"Suscriptor # " + this.id + " recibió una nueva edición: " + edicion
);
}
}
// Crea tres instancias de "Subscriptor"
const subscriptor1 = new Subscriptor(1);
const subscriptor2 = new Subscriptor(2);
const subscriptor3 = new Subscriptor(3);Ahora viene la parte clave que completa el ejemplo: suscribir el método buzon de cada instancia. Como notify invoca cada suscriptor con item.call(this, event), necesitamos que this dentro de buzon siga apuntando a la instancia correcta. Para eso usamos .bind, que fija el contexto:
// Suscribe el método "buzon" de cada instancia, fijando su contexto con .bind
periodico.subscribe(subscriptor1.buzon.bind(subscriptor1));
periodico.subscribe(subscriptor2.buzon.bind(subscriptor2));
periodico.subscribe(subscriptor3.buzon.bind(subscriptor3));
// Notifica a todos los suscriptores la nueva edición
periodico.notify("Edición de Junio");Al ejecutarlo verás algo como esto:
Se ha creado el suscriptor #: 1
Se ha creado el suscriptor #: 2
Se ha creado el suscriptor #: 3
Suscriptor # 1 recibió una nueva edición: Edición de Junio
Suscriptor # 2 recibió una nueva edición: Edición de Junio
Suscriptor # 3 recibió una nueva edición: Edición de JunioNota: Guarda la función enlazada si luego quieres cancelar la suscripción de un suscriptor. Cada llamada a .bind crea una función nueva, así que unsubscribe debe recibir exactamente la misma referencia que usaste al suscribirte:
>
``js const buzon1 = subscriptor1.buzon.bind(subscriptor1); periodico.subscribe(buzon1); //...más tarde periodico.unsubscribe(buzon1); ``
De esta forma podemos tener más control sobre los suscriptores y suscribirlos o cancelar su suscripción de mejor manera.
Conclusión
El patrón observador es fácil de implementar en JavaScript y su utilidad es casi inmediata. Con este patrón podemos manejar de manera efectiva la comunicación entre objetos, manteniendo el código limpio y organizado.
Ejercicios para practicar
- Implementa el patrón observador en una aplicación de mensajería instantánea, donde los usuarios se suscriben a un chat grupal y reciben notificaciones cuando hay nuevos mensajes.
- Crea un sistema de notificaciones de eventos para una aplicación de calendario, donde los usuarios puedan suscribirse a eventos y recibir recordatorios cuando estén próximos a realizarse.
- Diseña un sistema de monitoreo de temperatura para un conjunto de sensores, donde cada sensor es un observador y un controlador central es el publicador. Cuando la temperatura cambia en un sensor, el controlador es notificado y puede tomar decisiones en función de los cambios.
Resumen en 3 puntos
- El patrón observador es un patrón de comportamiento de la programación orientada a objetos que facilita la comunicación entre objetos, permitiendo notificar a unos objetos (los suscriptores) cuando cambia el estado de otro (el publicador).
- En JavaScript podemos implementar el patrón observador mediante clases y métodos como
subscribe(),unsubscribe()ynotify(). - Usar el patrón observador en tus proyectos te permitirá tener un control más granular y mantener tu código limpio y organizado.
Eso es todo, espero que este post te haya sido de utilidad y lo puedas aplicar a algún proyecto que tengas en mente. Si tienes alguna duda, sugerencia o quieres compartir tus experiencias implementando este patrón, no dudes en dejarme un comentario. Y recuerda que si te gustó, también puedes compartirlo usando los links a las redes sociales aquí abajo. ¡Buena suerte en tus proyectos!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.