Observer Pattern

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

EL observer pattern es un patrón de diseño utilizado por React y Redux.

Un patrón de diseño expresa una solución reutilizable para un problema habitual en el desarrollo de software. Un patrón de diseño no es una implementación de código acabada, sino una especie de plantilla o modo de proceder que podrá implementarse de varias formas

El problema que se encarga de resolver el Observer Pattern es que cuando en una aplicación cambie cierto valor todos los objetos vinculados a este cambio de estado sean notificados. Para ello, tendremos un estado global en el que almacenaremos las propiedades de cuyos cambios queremos ser notificados. Cuando cualquier propiedad de dicho estado cambie, todas las funciones vinculadas ( suscritas ) al observer serás notificadas.

El observer será un objeto que almacenará el estado global de la aplicación ( state ) y de notificar a todas las funciones suscritas de los cambios en el mismo.

A continuación tienes una implementación minimalista del observer pattern. En este ejemplo, un único número será almacenado en el state.

El método createObserver nos devolverá el objeto observador al que se van a suscribir todas las funciones para recibir una notificación de que algo de su propiedad state ha cambiado. Por tanto, este método devolverá un objeto con los métodos:

  • suscribe: para notificar a todos los objetos suscritos de los cambios producidos en el state. Este método recibe una función que añadiremos al grupo de funciones al que voy a llamar cuando el estado cambie.
  • updateState: para actualizar el valor del state. Este método llamará al método notifyChanges que se encargar´´a de actualizar el state para todas las funciones suscritas.
  • getState: para acceder al valor almacenado en el state.

firstSuscriber es el nombre de la función que vamos a suscribir al observer y que será notificada de cuando haya cambios en el state.

En nuestro ejemplo, vamos a actualizar el state cada vez que el usuario pulse un botón. Por tanto, llamaremos a la función callToObserverUpdateState cada vez que el usuario pulse un botón para actualizar el state.


const createObserver = (initialState) => {
    let state = initialState, nextSubscriberIndex = 0;

    const subscribers = {};

    const subscribe = (listener) => {
        if (typeof listener !== 'function') return;
        // suscriberIndex almacena la posición en la que nos estamos suscribiendo ( dentro de la colección de listeners que ya tenía el objeto)
        const subscriberIndex = nextSubscriberIndex;
        subscribers[subscriberIndex] = listener;
        nextSubscriberIndex++;
        listener(state);

        // Devolvemos una función con la que nos podemos desuscribir
        return () => delete subscribers[subscriberIndex]
    }

    const notifyChanges = () => {
        for (const key in subscribers) {
            const subscriber = subscribers[key];
            subscriber(state);
        }
    }

    // Cuando actualizamos el state evaluamos si su nuevo valor coincide con el anterior. Si coinciden, no hacemos nada. Si no coinciden, notificamos a todos los suscribers que el state ha cambiado.
    const updateState = (nextState) => {
        if (state === nextState) return
        state = nextState;
        notifyChanges();
    }

    return {
        subscribe,
        updateState,
        getState: () => state
    }
}

const observer = createObserver(0);

const firstSuscriber = (state) => {
    console.log("FIRST_LISTENER:", state)
}


observer.subscribe(firstSuscriber);

const callToObserverUpdateState = () => {
    const state = observer.getState();
    const newState = state + 1;
    observer.updateState(newState);
    document.querySelector('.state').innerHTML = newState;
}

Añadiendo un segundo suscriptor

Vamos a suscribir la función secondSuscriber. En este caso, además, llamaremos a la función unsuscribe al pulsar otro botón, de tal forma que dicha función dejará de ser notificada de los cambios.

const secondSuscriber = (state) => {
    console.log("SECOND_LISTENER: ", state);
}
unsubscribe = observer.subscribe(secondSuscriber);

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.