Patrón Observer (parte 1)
Tags: javascript, webdev, tutorial, typescript
Published: 2021-10-18 04:05:32

Patrón observer uno de los patrones que hace que parezcas un mago con el código, seria como:

> Mira voy a ingresar un dato aquí y va a aparecer algo allá 😌

pero bueno, si haz escuchado sobre rxjs y programación reactiva Pero bueno, si has escuchado sobre rxjs y programación reactiva supongo que ya sabes un poco sobre esto, pero si has utilizado la librería sin conocer el patrón, ven y peleamos, vale no 😁, de hecho yo también lo hice y pues tan mal no me fue. Aun así creo que aprender el patrón he implementarlo por ti mismo hace que todo sea un poco más fácil y abre más posibilidades.

Por cierto todo lo siguiente, implementación y explicación es mi manera de aprenderlo es más te aconsejo leer primero este maravilloso [artículo](https://refactoring.guru/es/design-patterns/observer).

### ¿Que es el patrón observer?

Es un patrón de comportamiento  que define un mecanismo de suscripción para notificar a una serie de objetos sobre cualquier cambio que le suceda al objeto que están observando.

Lo primero que se nos puede venir a la mente aquí, puede ser YouTube y las subscripciones, las notificaciones de tu red social favorita, etc. Con estos ejemplos podemos analizar el hecho de que el patrón observer le da la posibilidad de elegir que tipo de notificaciones recibir al sujeto, por ejemplo en YouTube cada vez que me suscribo a un canal voy a estar recibiendo notificaciones de cuando subió un vídeo este canal en particular.  

### Implementación:

Bien juguemos un poco con la lógica de suscripcion en YouTube.

EL patrón Observer consta de un notificador que a la vez  es el sujeto el cual contiene el estado que es notificado a las respectivas suscripciones. En este caso nuestro sujeto será YouTube.


```tsx
class Youtube {
  constructor() {}

   // notifies you when a video has been uploaded
	notify(notify: any): void {}

  // register a suscription 
  suscribe(sub: any): void {} 
  unsuscribe(sub: any): void {}
}
```

Aquí tenemos a nuestro Sujeto con los métodos necesarios para manejar las suscripciones, pero estos reciben parámetros tipo `any`. Arreglemos eso:

```tsx
interface Subscriber<T> {
  update(event: T): void;
}

export type Notification = {
  nameVideo: string;
  channel: string;
  date?: Date;
};
// suscription parameters
type SubscriptionInfo = {
  id: string;
  channel: string;
};
```

La parte interesante aquí es `Subscriber` que es la interfaz que nos va a ayudar a definir el método que se llama cuando ocurre un evento en este caso cuando se sube un video al canal. 

Ahora implementemos esa suscripción.

```tsx
class YoutubeSubscription implements Subscriber<Notification> {
  private sub: SubscriptionInfo;

  constructor(sub: SubscriptionInfo) {
    this.sub = sub;
  }
  getSub() {
    return this.sub;
  }
	// this method is called when the subject wants to notify and event
  update(event: Notification): void {
    console.log(
      ` (${event.date.toISOString()}) ${event.channel} uploaded a new video : ${
        event.nameVideo
      }`
    );
  }
}
```

Ahora ya tenemos la suscripción  y el cuerpo del evento en este caso es `Notification` y la implementación del método `update`.

Ahora veamos el cambio en la clase `Youtube`

```tsx
class Youtube {
  constructor() {}

  notify(notify: Notification): void {}

  suscribe(sub: YoutubeSubscription): void {}
  unsuscribe(sub: YoutubeSubscription): void {
      
  }
}
```

Listo ahora ya no tenemos `any` y tenemos implementado nuestro sujeto el cual ahora llamaremos Observable. 

Anteriormente creamos un método `createDatabase`  que fabricaba un objeto `Database`  con algunos métodos útiles  para manejar nuestros datos puedes ver el código [aquí](https://stackblitz.com/edit/typescript-lekhux?file=factory%2Fdatabase.ts). Entonces,  vamos a usar este método para los canales.

```tsx
interface Chanel extends BaseRecord {
  name: string;
}

class Youtube {
  channels = createDatabase<Chanel>({
    typeId: 'incremental'
  });

  constructor() {
    this.channels.insert({
      name: 'leobar'
    });
    this.channels.insert({
      name: 'nose'
    });
  }

  notify(notify: Notification): void {}

  suscribe(sub: YoutubeSubscription): void {}
  unsuscribe(sub: YoutubeSubscription): void {}
}
```

Ahora tenemos los canales los cuales deberían de tener un conjunto de suscripciones, voy a manejar esa lógica por aparte manteniendo en memoria las suscripciones activas con un identificador, en este caso el nombre del canal.

```tsx
class Youtube {
  suscriptions: Map<string, YoutubeSubscription[]> = new Map();

  channels = createDatabase<Chanel>({
    typeId: 'incremental'
  });

  constructor() {
    this.channels.insert({
      name: 'leobar'
    });
    this.channels.insert({
      name: 'nose'
    });
  }

  notify(notify: Notification): void {}

  suscribe(sub: YoutubeSubscription): void {}
  unsuscribe(sub: YoutubeSubscription): void {}
}
```

En este caso estoy manejando las suscripciones con la estructura [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) . Ahora agreguemos lógica a los métodos.

```tsx
class Youtube {
  // .. code
  notify(notify: Notification): void {
    this.suscriptions.get(notify.channel).forEach(d => d.update(notify));
  }
  get getChannels() {
    return this.channels.findAll({});
  }
  suscribe(sub: YoutubeSubscription): void {
    // if channel does not exist throw an exception
    if (this.channels.findAll({ name: sub.getSub().channel }).length == 0) {
      throw new Error('This channel does not exist');
    }
    let subs: YoutubeSubscription[] = [];
    if (this.suscriptions.has(sub.getSub().channel)) {
      subs = this.suscriptions.get(sub.getSub().channel);
    }
    subs.push(sub);
    this.suscriptions.set(sub.getSub().channel, subs);
  }
  unsuscribe(sub: YoutubeSubscription): void {
    let channelSubs = this.suscriptions.get(sub.getSub().channel);
    if (channelSubs) {
      channelSubs = channelSubs.filter(sub => sub !== sub);
      this.suscriptions.set(sub.getSub().channel, channelSubs);
      console.log(`${sub.getSub().id} Unsuscribed`);
      console.log('Suscribers :' + channelSubs.length);
      console.log(channelSubs);
    }
  }
}
```

Listo. Ahora tenemos implementando el patrón. `Youtube` es nuestro **Observable** y `YoutubeSubscription`  lo vamos a llamar ***observer***.Ahora nos damos cuenta que estamos tratando con una relación de uno a muchos

![relation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27g1607ymue0ubco1vri.png)

Ahora vamos a probar lo que hemos hecho:

```tsx
const youtube = new Youtube();

const leobarSub = new YoutubeSubscription({
  channel: 'leobar',
  id: '1'
});

const noseSubscription = new YoutubeSubscription({
  channel: 'nose',
  id: '2'
});

youtube.suscribe(leobarSub);

youtube.suscribe(noseSubscription);

let cont = 0;

const channels = youtube.getChannels.map(d => d.name);

setInterval(() => {
  const ramdom = Math.floor(Math.random() * channels.length);
  youtube.notify({
    channel: channels[ramdom],
    nameVideo: 'video nro:' + cont,
    date: new Date()
  });
  if (cont === 5) {
    youtube.unsuscribe(leobarSub);
  }
  if (cont == 8) {
    youtube.unsuscribe(noseSubscription);
  }
  cont++;
}, 2000);
```

Lo que hecho es instancia nuestro **Observable,** agregarle dos suscripciones y cada cierto tiempo notificar aleatoriamente la que se subió un video.

![console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slbld5gjotcbvn1vl161.gif)

Hasta aquí hemos logrado implementar el patrón Observable de una manera que veamos cuál es su sentido de existir y su utilidad. Dejaré esta parte hasta aquí, En segunda parte quiero hacer una implementación más generalizada y ver como este patrón nos abre las puertas hacia la programación reactiva 😌.

Código completo [aquí](https://stackblitz.com/edit/typescript-lekhux?file=observer%2Fyoutube.ts):


### Links

Si todavía tienes dudas sobre como funciona este patrón puedes consultar los siguientes links:

- [https://refactoring.guru/es/design-patterns/observer](https://refactoring.guru/es/design-patterns/observer)
- [https://es.wikipedia.org/wiki/Observer_(patrón_de_diseño)#:~:text=Observador (en inglés%3A Observer),cambio a todos los dependientes](https://es.wikipedia.org/wiki/Observer_(patr%C3%B3n_de_dise%C3%B1o)#:~:text=Observador%20(en%20ingl%C3%A9s%3A%20Observer),cambio%20a%20todos%20los%20dependientes).