Entendiendo el patrón observador "Observer Pattern" en Javascript

El patrón observador es uno de los patrones de diseño de software más usado en Javascript. De el se extienden importantes aplicaciones que pueden ayudar a definir mejores arquitecturas en aplicaciones web, por lo que su uso y estudio es altamente recomendado. En este post aprenderemos como implementarlo en Javascript y en que situaciones lo podemos usar.

En primer lugar el patrón observador se define como un patrón de comportamiento es decir que dentro del mundo de la programación orientada a objetos es un patrón responsable por la comunicación entre objetos.

En segundo lugar el patrón observador también puedes encontrarlo como el patrón publicador-subscriptor o modelo-patrón y nos da una idea básica de lo que hace. En términos simples este patrón permite la notificación a objetos (subscriptores u observador) al cambio de otro objeto (publicador).

Veamos entonces un pequeño diagrama con las responsabilidades de los involucrados en este patrón. En primer lugar el publicador debe notificar a los subscriptores o observadores que algo cambió, y en segundo lugar los subscriptores deben poder subscribirse o de suscribirse del publicador en cualquier momento:

¿Cómo podemos entonces implementarlo en Javascript?, empecemos implementando una clase llamada Publicador que contenga los métodos subscribe(), unsubscribe(), y notify().

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Publicador {

constructor() {
this.subscriptors = \[\];
}

subscribe(subscriptor) {
this.subscriptors.push(subscriptor);
}

unsubscribe(subscriptor) {
this.subscriptors = this.subscriptors.filter( item => item !== subscriptor );
}

notify(event) {
this.subscriptors.forEach( item => {
item.call(this, event);
});
}
}

Como verás hemos manejado la lista de subscriptores con un array de Javascript como propiedad de la clase así es posible fácilmente subscribir y des-subscribir subscriptores. También la función notify itera sobre cada uno de los subscriptores y se encarga de invocarlos con el evento.

Ahora imaginemos que usaremos esta definición de Publicador para un periódico que regularmente publica nuevas ediciones.

  • js
1
const periodico = new Publicador();

Bien, pensemos un poco más acerca de los clientes. Estos van a necesitar saber cuando llegue una nueva versión del periódico. Inicialmente pensemos en que los clientes son funciones:

  • js
1
2
3
4
5
6
7
function Observer(edicion) {
console.log("LLegó una nueva edición con el nombre de: " + edicion);
}

periodico.subscribe(Observer);
periodico.subscribe(Observer);
periodico.notify("Nueva edicion");

Con esta definición anterior al ejecutarlo obtenemos algo así:

“LLegó una nueva edición con el nombre de: Nueva edicion”
“LLegó una nueva edición con el nombre de: Nueva edicion”

Si queremos ser conscientes de que cliente recibió que edición del periódico podemos retocar un poco las definiciones de la función notify y de la función observer:

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Publicador {
...
notify(event) {
let index = 0;
this.subscriptors.forEach( item => {
item.call(this, index, event);
index++;
});
}
...
}
...
function Observer(index, edicion) {
console.log("Al Observador #" +
index + " le llegó una nueva edición con el nombre de: " +
edicion);
}

De esta manera tenemos como output algo mucho mas entendible:

  • js
1
2
"Al Observador #0 le llegó una nueva edición con el nombre de: Nueva edicion"  
"Al Observador #1 le llegó una nueva edición con el nombre de: Nueva edicion"

Sin embargo como un mejor acercamiento podríamos definir una clase para los clientes que nos permita crear instancias de ella y tener un control mas granular. También debemos definir un método únicamente diseñado para escuchar por nuevas decisiones del periódico.

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
class Observador {
constructor(id) {
this.id = id;
console.log("Se ha creado el subscriptor #: " + id);
}
buzon(edicion) {
console.log("Subscriptor # " + this.id + " recibió una nueva edición: " + edicion);
}
}

const subscriptor1 = new Subscriptor(1);
const subscriptor2 = new Subscriptor(2);
const subscriptor3 = new Subscriptor(3);

De esta forma podemos tener más control sobre los subscriptores y podemos subscribirlos y des-subscribirlos de mejor manera. A continuación verás como publicar multiples ediciones de un perioido así como la habilidad suscribir y des-suscribir clientes:

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
periodico.subscribe(subscriptor1);
periodico.subscribe(subscriptor2);
periodico.notify("Nueva edicion");
periodico.subscribe(subscriptor3);
periodico.notify("Segunda edicion");
periodico.unsubscribe(subscriptor1);
periodico.notify("Tercera edicion");
periodico.subscribe(subscriptor2);
periodico.notify("Nueva edicion");
periodico.subscribe(subscriptor3);
periodico.notify("Segunda edicion");
periodico.unsubscribe(subscriptor1);
periodico.notify("Tercera edicion");

Y obtenemos la siguiente salida:

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"--- Primera edición ---"

"Subscriptor # 1 recibió una nueva edición: Nueva edicion"

"Subscriptor # 2 recibió una nueva edición: Nueva edicion"

"--- Segunda edición ---"

"Subscriptor # 1 recibió una nueva edición: Segunda edicion"

"Subscriptor # 2 recibió una nueva edición: Segunda edicion"

"Subscriptor # 3 recibió una nueva edición: Segunda edicion"

"--- Tercera edición ---"

"Subscriptor # 2 recibió una nueva edición: Tercera edicion"

"Subscriptor # 3 recibió una nueva edición: Tercera edicion"

De esta manera queda totalmente completo el patrón observer. Como vez es muy fácil implementar el patrón observer y su utilidad es casi inmediata. A continuación podrás observar todo el código completo de este ejemplo:

  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Publicador {
constructor() {
this.subscriptors = \[\];
}

subscribe(subscriptor) {
this.subscriptors.push(subscriptor);
}

unsubscribe(subscriptor) {
this.subscriptors = this.subscriptors.filter( item => item !== subscriptor );
}

notify(event) {
this.subscriptors.forEach( item => {
item.buzon.call(item, event);
});
}
}

class Subscriptor {
constructor(id) {
this.id = id;
console.log("Se ha creado el subscriptor #: " + id);
}
buzon(edicion) {
console.log("Subscriptor # " + this.id + " recibió una nueva edición: " + edicion);
}
}

const periodico = new Publicador();

const subscriptor1 = new Subscriptor(1);
const subscriptor2 = new Subscriptor(2);
const subscriptor3 = new Subscriptor(3);

console.log("--- Primera edición ---");

periodico.subscribe(subscriptor1);

periodico.subscribe(subscriptor2);

periodico.notify("Nueva edicion");

console.log("--- Segunda edición ---");

periodico.subscribe(subscriptor3);

periodico.notify("Segunda edicion");

console.log("--- Tercera edición ---");

periodico.unsubscribe(subscriptor1);

periodico.notify("Tercera edicion");

Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente y que simplemente te haya ayudado a entender la naturaleza del patrón observer. déjame un comentario si lograste implementarlo, si quieres añadir alguna otra funcionalidad o si tienes alguna duda no dudes en dejarme un comentario en la parte de abajo, recuerda que si te gustó también puedes compartir usando los links a las redes sociales en la parte de abajo.

Copyrights © 2018 Sebastian Gomez. All Rights Reserved.

Sobre mí

sebastianMi nombre es Sebastián Gómez, soy ingeniero de sistemas e Informática y Magister en Ingeniería de Sistemas de la Universidad Nacional de Colombia.

Actualmente trabajo en Globant como Web UI Developer con énfasis en aplicaciones híbridas y cross compiladas. Soy el organizador del Google Developers Group de Medellín, así que contactame si quieres dar alguna charla o participar actuamente de esta comunidad.

He participado en una Startup Colombiana llamada SponzorMe al lado de Carlos Rojas y fuí participante de Startup Chile a pesar de no haber continuado con esta startup me apasiona el emprendimiento y me gusta aconsejar y ayudar startups como mentor técnico. También he trabajado en empresas Americanas como StudioHyperset en Estados Unidos y para Measured Medium. Mi interés y mi experiencia es el desarrollo de web y móvil full stack como Front-end con Javascript. Me apasiona desarrollar software, escribir código y enseñar lo que aprendo día a día.

También he trabajado como profesor en diferentes universidades en Medellín Colombia, con tematicas relacionadas con la Inteligencia Artificial, Bases de datos, programación orientada a objetos, minería de datos, desarrollo de software, desarrollo móvil y desarrollo web.

Me encanta escribir código rápido y prototipar de una manera accelerada si quieres ver que hago día a día puedes darle un vistazo a mi codepen:  https://codepen.io/seagomezar/.

Todos los días trato de crear o participar en proyectos, la mayoría open source, así que puede chequear mi GitHub:  https://github.com/seagomezar.

Mi áreas de investigación académica son: Ingeniería de software, Ingeniería de requisitos, procesamiento del lenguaje natural, Ontologías, Bases De Datos,  Machine Learning, Seguimiento de trayectorias y Modelamiento matemático de formaciones.

Estas son algunas de mis publicaciones académicas mas recientes: