loading...
Cover image for Pub-Sub Pattern: conceito e exemplo

Pub-Sub Pattern: conceito e exemplo

jucian0 profile image Juciano Updated on ・4 min read

Alguns meses atrás em uma publicação sobre Observable eu expliquei nas minhas palavas como eu entendia o padrão e dei um exemplo utilizando a linguagem javascript. E nesta nova postagem vou tentar explicar de forma simples o padrão publisher-subscriber, e ao ler às duas postagens acredito que em sua mente vai ficar bem claro as diferenças existentes nesses dois exemplos.

Link do primeiro post sobre Observable:

https://dev.to/jucian0/observer-pattern-conceito-e-exemplo-4e8g

Este padrão pode ser muito útil quando você deseja enviar um certo evento e deseja que somente os interessados nesse determinado assunto sejam notificados. Nesse ponto percebemos uma diferença com o Observable, pois este não possui um em sua lógica de implementação a condição de selecionar o evento que deseja publicar, e nem de pode escolher o evento que deseja se inscrever.

Enquanto no Observable você dispara um único evento e todos os inscritos são notificados do evento, no Publisher-Subscriber é necessário definir o evento que será publicado e que deseja se inscrever deve declarar o evento que possuí interesse.

Uma analogia

Aproveitando o post anterior em que usei o exemplo de uma publicação de vaga de emprego vou modificar um pouco a analogia para servir ao nosso proposito.

Digamos que uma certa empresa chamada Atric esteja buscando aumentar seu quandro de funcionários, ela precisa de uma série de profissionais tais como, caldeireiro, torneiro mecânico, motorista. Bem pensando em tornar isso conhecido em toda a cidade ela anuncia em um jornal essas vagas, então vários interessados comparecem à empresa e se inscrevem, uns se inscrevem para vaga de motorista, outros para caldeireiro e por fim outros para torneiro mecânico. A empresa garante a todos os candidatos de todas as vagas que o processo seletivo sera muito transparente, e que ao final todos vão ser informados sobre o candidato escolhido de cada vaga.
No final do processo a empresa prepara um e-mail para os candidatos a motorista, outro para torneiro mecânico e por fim para caldeireiro, e em seguida envia os e-mails com os dizeres:

Agradecemos sua inscrição e interesse em trabalhar em nossa empresa, recebemos muitas inscrições para a vaga de X.
O candidato escolhido é o Jose Antonio da Silva.

Essa é uma explicação bem simples da ideia do Publisher-Subscriber, perceba que cada tipo de vaga é um tipo de evento, e os profissionais de cada categoria são os inscritos, e cada um recebeu o e-mail especifico para a resposta do tipo de vaga que o mesmo havia se candidatado.

pub-sub

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

class PubSub {
  constructor() {
    this.subscribers = {};
  }

  subscribe(event, fn) {
    if (Array.isArray(this.subscribers[event])) {
      this.subscribers[event] = [...this.subscribers[event], fn];
    } else {
      this.subscribers[event] = [fn];
    }
    return () => {
      this.unsubscribe(event, fn);
    };
  }

  unsubscribe(event, fn) {
    this.subscribers[event] = this.subscribers[event].filter(
      (sub) => sub !== fn
    );
  }

  publish(event, data) {
    if (Array.isArray(this.subscribers[event])) {
      this.subscribers[event].forEach((sub) => {
        sub(data);
      });
    }
    return false;
  }
}

export default new PubSub();
  • 1 a 4 — Começo criando uma classe com o nome PubSub e em seu construtor eu adicionei um objeto vazio na propriedade subscribers. Esse objeto vai armazenar os eventos cadastrados, cada evento sendo uma propriedade do objeto e cada propriedade recebendo um array de subscribers.
  • 6 a 15 — O método subscribe recebe uma string e uma função como parâmetro, essa string sera o nome do evento e a função é um subscriber, em seguida é preciso validar se o array de subscribers esta vazio para distribui-lo em um novo array junto a função passada por parâmetro ou colocar a função em um array vazio e por fim atribui a propriedade subscribers.
  • 17 a 21— Unsubscribe é o método responsável por filtrar os subscribers, ele recebe o evento e o subscriber por parâmetro e remove o mesmo da lista.
  • 17 a 22 — Publish é 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.

E por fim como na postagem anterior deixo um exemplo de uso.

import "./styles.css";
import PubSub from "./PubSub";

const firstInput = document.getElementById("first-input");
const secondInput = document.getElementById("second-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}`);

firstInput.addEventListener("input", (e) =>
  PubSub.publish("first-event", e.target.value)
);

secondInput.addEventListener("input", (e) =>
  PubSub.publish("second-event", e.target.value)
);

firstSubscriberBtn.addEventListener("click", (e) => {
  e.preventDefault();
  PubSub.subscribe("first-event", firstText);
});

firstUnSubscriberBtn.addEventListener("click", (e) => {
  e.preventDefault();
  PubSub.unsubscribe("first-event", firstText);
});

secondSubscriberBtn.addEventListener("click", (e) => {
  e.preventDefault();
  PubSub.subscribe("second-event", secondtText);
});

secondUnSubscriberBtn.addEventListener("click", (e) => {
  e.preventDefault();
  PubSub.unsubscribe("second-event", secondtText);
});
  • 4 a 13 — Estou selecionando os elementos html.
  • 15 a 16 — Duas funções simples que atribuem valores passados por parâmetro para os elementos selecionados anteriormente.
  • 18 a 24 — Estou me inscrevendo no evento adicionando uma função como ouvinte do evento “input” para cada input e dentro dessas funções eu publico os eventos “first-event” e “second-event” com os valores dos inputs.
  • 25 a 44 — Repito o processo anterior, mas dessa vez eu estou me inscrevendo nos eventos, primeiro me inscrevo no “first-event”, faço isso passando uma função como ouvinte do evento de click de um botão, dessa forma ao clicar nesse botão estarei me inscrevendo no evento mencionado. E esse processo é repetido para remover a inscrição e criar a inscrição para o segundo evento.

Veja esse exemplo funcionando

https://codesandbox.io/s/pub-sub-7qvkr?from-embed

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.

Discussion

pic
Editor guide