DEV Community

Cover image for Observer Pattern: Conceito e exemplo
Jucian0
Jucian0

Posted on • Updated on

Observer Pattern: Conceito e exemplo

Nesta postagem vou tentar explicar e exemplificar um padrão muito utilizado por desenvolvedores, mesmo que muitos desenvolvedores que estão iniciando suas carreiras não entendam do que se trata pelo nome ou mesmo não saibam como esse padrão é implementado com certeza a maioria utiliza ele através de bibliotecas. Vamos falar sobre o pattern Observer.
Esse padrão é muito útil quando vários componentes de software estão interessados em um determinado evento e dessa forma todos eles se inscrevem para serem notificados quando o evento ocorrer e terem acesso às mudanças realizadas pelo evento.

Get in touch with me on Twitter

Uma analogia

Quando eu estava aprendendo sobre esse padrão li varias analogias que tentavam exemplificar de forma simples o funcionamento do mesmo, a mais interessante é a de um concurso cultural, vamos la:

Uma empresa chamada Atric queria premiar um funcionário com uma viagem, e para isso a mesma criou um concurso cultural.
A ideia era bem simples, os funcionários deveriam criar uma frase descrevendo "Como seria sua viagem perfeita", e essa resposta deveria ser enviada por e-mail, e no final de duas semanas a empresa iria devolver um email com o resultado. Bem no final de duas semanas a empresa respondeu os funcionários com o seguinte e-mail:

Obrigado por participar de nosso concurso cultural, nos recebemos muitas inscrições.
O funcionário que ganhou o prêmio é o Juciano Barbosa.

Essa é uma explicação bem simples da ideia do Observer Pattern, Quando o evento acontece todos os inscritos são notificados.

O concurso cultural era uma Observable e os participantes eram os observers.
alt text

Veja a seguir como fica a implementação desse padrão."

class Observable {
  constructor() {
    this.observers = [];
  }

  subscribe(fn) {
    this.observers = [...this.observers, fn];
    return () => {
      this.unsubscribe(fn);
    };
  }

  unsubscribe(fn) {
    this.observers = this.observers.filter(observer => observer !== fn);
  }

  notify(data) {
    this.observers.forEach(observer => {
      observer(data);
    });
  }
}

export default new Observable();
Enter fullscreen mode Exit fullscreen mode
  • 1 a 4 - Começo criando uma classe com o nome Observable e em seu construtor eu adicionei um array vazio na propriedade observers. Esse array vai armazenar uma lista de observers que serão cadastrados.
  • 26 a 11- O método subscribe recebe uma função como argumento, esse argumento é um observer, em seguida atribuo a propriedade observers um novo array contendo o valor de observer mais o novo observer que recebi como argumento. Dentro da mesma função eu retorno o método unsubscribe, em alguns casos isso pode ser conveniente para se remover a inscrição.
  • 13 a 15 - Unsubscribe é o método responsável por filtrar os observers, ele recebe o observer como argumento e remove o mesmo da lista.
  • 17 a 22 - Notify é o método que vai percorrer a lista de observers e executar cada um passando os dados que o mesmo recebe como argumento.
  • 23 - No final exporto um objeto da classe para não precisar usar new onde o recurso for utilizado.

É um código muito simples, mas que pode ajudar muito em vários cenários, abaixo deixo um exemplo simples de uso para entender como pode ser utilizado:

import Observable from "./Observer";

const input = document.getElementById("text-input");
const firstSubscriberBtn = document.getElementById("first-subscriber-btn");
const secondSubscriberBtn = document.getElementById("second-subscriber-btn");
const firstUnSubscriberBtn = document.getElementById("first-un-subscriber-btn");
const secondUnSubscriberBtn = document.getElementById(
  "second-un-subscriber-btn"
);
const textFirstSubscriber = document.getElementById("first-subscriber");
const textSecondSubscriber = document.getElementById("second-subscriber");

const firstText = e => (textFirstSubscriber.innerText = `${e}`);
const secondtText = e => (textSecondSubscriber.innerText = `${e}`);

input.addEventListener("input", e => Observable.notify(e.target.value));

firstSubscriberBtn.addEventListener("click", e => {
  e.preventDefault();
  Observable.subscribe(firstText);
});

secondSubscriberBtn.addEventListener("click", e => {
  e.preventDefault();
  Observable.subscribe(secondtText);
});

firstUnSubscriberBtn.addEventListener("click", e => {
  e.preventDefault();
  Observable.unsubscribe(firstText);
});
secondUnSubscriberBtn.addEventListener("click", e => {
  e.preventDefault();
  Observable.unsubscribe(secondtText);
});
Enter fullscreen mode Exit fullscreen mode
  • 4 a 12 - Estou selecionando os elementos html.
  • 14 e 15 - Duas funções simples que atribuem valores recebidos como argumentos para os elementos selecionados anteriormente.
  • 17 - Nesta linha estou adicionando um evento de input no elemento input e usando o Observer.notify para disparar notificações para os observers a cada evento de input.
  • 18 a 27 - Nestas linhas estou adicionando evento de click nos botões que vão inscrever as funções firstText e secondText como observers.
  • 29 a 36 - Nestas linhas estou adicionando evento de click nos botões que vão remover a inscrição das funções firstText e secondText como observers.

Veja esse exemplo funcionando

https://codesandbox.io/s/github/Jucian0/observer-pattern

Agora que temos o entendimento de como o pattern Observer funciona, podemos perceber que muitas ferramentas usam um princípio parecido para funcionar, por exemplo: imagine que cada observer é um componente, e o método notify é uma espécie de dispatch, poderíamos construir um gerenciamento de estado rudimentar, nada comparado ao redux, mas que nos daria uma ideia básica de como funciona um gerenciador de estado.
Outro detalhe importante é lembrar que no ecossistema de javascript temos uma biblioteca poderosa para criar e gerenciar Observers a Rxjs, com ela é possível criar um observable facilmente e a cada mudança no dado ou evento observado pode ser adicionado operadores que fazem coisas incríveis.

Conclusão

Mesmo que você não precise escrever esse patern para utilizar no dia a dia é muito importante entender como ele funciona, pois, muitas bibliotecas que usamos comumente utilizam ele para implementar suas soluções.
Na próxima postagem sobre o tema, abordarei como utilizar ele no contexto do react.

Top comments (4)

Collapse
 
kurybr profile image
Jorge Rafael

Excelente Material ! mandou super bem !

Collapse
 
jucian0 profile image
Jucian0

muito obrigado

Collapse
 
urielsouza29 profile image
Uriel dos Santos Souza

Isso não seria pub/sub?

Collapse
 
jucian0 profile image
Jucian0