loading...
Cover image for Observer Pattern: Conceito e exemplo

Observer Pattern: Conceito e exemplo

jucian0 profile image Juciano ・4 min read

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.

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 da vaga de emprego, vamos la:
Digamos que uma certa empresa chamada Atric esteja buscando aumentar seu quandro de funcionários por isso publica a vaga em um jornal, então vários interessados comparecem à empresa e se inscrevem para a vaga. A empresa garante a todos os candidatos que o processo seletivo sera muito transparente, e que ao final todos vão ser informados sobre o candidato escolhido.
No final do processo a empresa dispara um e-mail para todos os candidatos com os dizeres:
"Agradecemos sua inscrição e interesse em trabalhar em nossa empresa, recebemos muitas inscrições.
O candidato escolhido é o Jose Antonio da Silva
Essa é uma explicação bem simples da ideia do Observer Pattern, Quando o evento acontece todos os inscritos são notificados.

A vaga de emprego era uma observable e os candidatos 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();
  • 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 parâmetro, esse parâmetro é um observer, em seguida atribuo a propriedade observers um novo array contendo o valor de observer mais o novo observer que recebi como parâmetro. 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 por parâmetro 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 por parâmetro.
  • 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 "./styles.css";
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);
});
  • 4 a 12 - Estou selecionando os elementos html.
  • 14 e 15 - Duas funções simples que atribuem valores passados por parâmetro 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/upbeat-grothendieck-wyyp2?from-embed

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 dentro do 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 dentro do contexto do react.

Posted on by:

jucian0 profile

Juciano

@jucian0

Escrevo sobre desenvolvimento de software e UI, atualmente trabalho focado em aplicações front end e estou estudando Rust.

Discussion

markdown guide