O Observer Pattern é um padrão muito popular em basicamente todos os tipos de aplicativos JavaScript. A ideia por trás desse pattern é a seguinte: nós temos um objeto central que é chamado de subject, onde o mesmo mantém uma lista de dependentes, que são chamados de observers. Os observers são os objetos que estão interessados em observar o subject.

Uma vez que o estado do nosso subject muda, ele notifica todo mundo que está o observando. Não se importando, com o que cada observer vai fazer com esta informação, ele simplesmente diz “meu estado foi alterado, se vira e faz o que você tem que fazer”. Os observers com posse da informação podem fazer qualquer coisa, isso vai depender muito da sua aplicação.

A princípio vamos conhecer os métodos do Observer Pattern:

  • Subscribe: Este método é utilizado para inscrever um observer;
  • Unsubscribe: Este é empregado para remover um observer;
  • Notify: Método aplicado para notificar todos os observers que estão observando.

Para facilitar o entendimento vamos fazer uma analogia

  • Imagine que você gosta de receber notificações sobre promoções de uma determinada loja, então se cadastra no site da loja, desse modo você passa a receber diariamente e-mails de promoções;
  • Porém, agora você já não acha mais interessante receber esses e-mails, então você cancela sua inscrição no site;
  • Apesar disso, neste meio tempo, outras pessoas estão se cadastrando e cancelando cadastros além de você.

No padrão observer a loja seria o subject e você seria o observer.

Prática

A princípio temos que entender que este padrão de projeto, na sua grande maioria, segue um “contrato” que em linguagens orientadas a objetos é representado por interfaces. Isso não quer dizer que nós não possamos implementar com classes ou funções, inclusive nosso exemplo vai ser construído com funções.

O que vamos construir?

Nosso exemplo vai ser bem simples, mas por outro lado, vai te agregar muito conhecimento.

Em nossa aplicação vamos ter um input e dez parágrafos. Quando o input mudar, os parágrafos vão ser notificados e exibir exatamente o que for digitado no input, de forma instantânea.

Vamos construir tudo em um mesmo arquivo por questões simples e didáticas, ok?

Documento index.html

Vamos ver um pouco de HTML E CSS apenas para estruturação e estilo da nossa página, mas não é o foco aqui.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html {
      height: 100vh;
    }

    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100%;
    }

    .container {
      background-color: #c3c3c3a8;
      border-radius: 8px;
      width: 40%;
      height: 80%;

      display: flex;
      /* justify-content: center; */
      align-items: center;
      flex-direction: column;
    }

    .container h1 {
      margin: 20px 0 20px 0;
    }

    .content {
      width: 100%;
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    input {
      height: 30px;
      width: 70%;
      border-radius: 8px;
      margin: 10px 0 30px 0;

      padding: 5px 10px;
    }

    p {
      margin: 10px 0 20px 0;
      font-size: 1.4rem;
    }
  </style>
</head>

<body>
  <div class="container">
    <h1>Padrão de Projeto Observer</h1>
    <div class="content">
      <input type="text">
      <p>Paragrafo número 1</p>
      <p>Paragrafo número 2</p>
      <p>Paragrafo número 3</p>
      <p>Paragrafo número 4</p>
      <p>Paragrafo número 5</p>
      <p>Paragrafo número 6</p>
      <p>Paragrafo número 7</p>
      <p>Paragrafo número 8</p>
      <p>Paragrafo número 9</p>
      <p>Paragrafo número 10</p>
    </div>
  </div>

  <script>
    
  </script>
</body>

</html>

Nosso projeto vai ficar com essa cara:

Layout

Criando um Subject

Dentro da tag <script></script>, vamos construir nossa função, que criará um subject.

function createSubject() {
      const state = {
        observers: [],
      }

      console.log(state.observers)

      function subscribe(observer) {
        if (!state.observers.includes(observer)) {
          state.observers.push(observer);

        }
      }

      function unsubscribe(observer) {
        state.observers = state.observers.filter(subscriber => subscriber !== observer)
      }

      function notifyAll(value) {
        console.log("Notifying " + state.observers.length + " observers")
        state.observers.forEach(observer => observer.update(value));
      }

      document.querySelector('input').addEventListener('keyup', handleKeyDown);

      function handleKeyDown(event) {
        notifyAll(event.target.value);
      }

      return {
        subscribe,
        unsubscribe
      }
    }

Feito isso, temos uma função que cria um estado interno com um array que armazenará todos os nossos observers.

Além disso, temos os métodos de inscrever um observer, remover um observer e notificar todos os observer.

Em seguida, começamos a escutar o evento do nosso subject propriamente dito, associado a função que chamará nosso método notifyAll() que notificará todos os nosso observers, e assim finalizamos a criação do nosso subject.

Criando um Observer

Ainda dentro da tag <script></script> vamos construir nossa função que será responsável por criar um observer.

function createObserver(observer) {

      function update(value) {
        observer.innerText = value;
      }

      return {
        update
      }
    }

Esta função é um pouco mais simples, porém terá um papel muito importante em nosso exemplo, que é atualizar todos os nossos observers.

Enfim, o que nos resta é criar nosso subject e todos os nossos observers já adicionando-os no state.

const inputSubject = createSubject();

const paragraphs = document.querySelectorAll('p')
    
    paragraphs.forEach((p, i) => {
      let paragraphObserver = `paragraph${i}`;
      paragraphObserver = createObserver(p)

      inputSubject.subscribe(paragraphObserver)
    })

Logo após a criação do inputSubject (que é o nosso subject), objeto que será observado, selecionamos todos os parágrafos do HTML e passamos por cada um deles criando nossos observers e adicionando-os as nosso state.

Por fim, só nós resta testar nosso exemplo:

Testando padrão de projeto
Testando nosso pequeno exemplo.

Conclusão

Esta é uma versão muito simples do Observer Pattern que, se absorvidos os conhecimentos passados, podem resolver muitos dos problemas atuais.

React e Vue usam este padrão e vejam quantos dos problemas atuais eles conseguem resolver; o Observer Pattern é muito poderoso e vale a pena ser estudado mais a fundo.

Se tiver interesse e quiser mais detalhes, leia este capitulo do livro Learning JavaScript Design Patterns.

Por fim, obrigado pela leitura e até a próxima! 🙂