padrões de Design JavaScript: o padrão Observer

em JavaScript, há um problema que surge com frequência. Você precisa de uma maneira de atualizar partes de uma página em resposta a determinados eventos, com os dados que eles fornecem. Digamos, por exemplo, a entrada do usuário que você projeta em um ou vários componentes. Isso leva a um monte de push-and-pull no código para manter tudo em sincronia.

é aqui que o padrão de design do observador pode ajudar-ele permite a ligação de dados de um para muitos entre os elementos. Esta ligação de dados unidirecional pode ser orientada a eventos. Com este padrão, você pode construir código reutilizável que resolve para suas necessidades específicas.

neste artigo, gostaria de explorar o padrão de design do observador. Isso o ajudará a resolver um problema comum que você vê no script do lado do cliente. Essa é uma ligação de dados unidirecional e orientada a eventos de um para muitos. É um problema que surge frequentemente quando você tem muitos elementos que devem estar sincronizados.

vou usar o ECMAScript 6 para ilustrar o padrão. Sim, haverá classes, funções de seta e constantes. Sinta-se à vontade para explorar esses tópicos por conta própria, se ainda não estiver familiarizado. Vou usar partes do ES6 que introduzem apenas açúcar sintático, por isso é portátil com ES5, se necessário.

e vou usar Test-Driven-Development (TDD) para trabalhar no padrão. Desta forma, você tem uma maneira de saber como cada componente é útil.

os novos recursos de idioma no ES6 criam algum código sucinto. Então, vamos começar.

o observador de eventos

uma visão de alto nível do padrão é assim:

EventObserver│ ├── subscribe: adds new observable events│ ├── unsubscribe: removes observable events|└── broadcast: executes all events with bound data

depois de desenvolver o padrão de observador, adicionarei uma contagem de palavras que a usa. O componente de contagem de palavras levará este observador e reunirá tudo.

para inicializar o EventObserver do:

class EventObserver { constructor() { this.observers = ; }}

comece com uma lista vazia de eventos observados e faça isso para cada nova instância. A partir de agora, vamos adicionar mais métodos dentro EventObserver para desenvolver o padrão de design.

o método de inscrição

para adicionar novos eventos faça:

subscribe(fn) { this.observers.push(fn);}

pegue a lista de eventos observados e empurre um novo item para a matriz. A lista de Eventos é uma lista de funções de retorno de chamada.

uma maneira de testar este método em JavaScript simples é a seguinte:

// Arrangeconst observer = new EventObserver();const fn = () => {};// Actobserver.subscribe(fn);// Assertassert.strictEqual(observer.observers.length, 1);

eu uso asserções de nó para testar este componente no nó. As mesmas afirmações existem exatamente como afirmações Chai também.

observe que a lista de eventos observados consiste em humildes retornos de chamada. Em seguida, verificamos o comprimento da lista e afirmamos que o retorno de chamada está na lista.

o método de Cancelamento de inscrição

para remover eventos do:

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

filtre da lista o que corresponder à função de retorno de chamada. Se não houver correspondência, o retorno de chamada permanecerá na lista. O filtro retorna uma nova lista e reatribui a lista de observadores.

para testar este bom método, Faça:

// Arrangeconst observer = new EventObserver();const fn = () => {};observer.subscribe(fn);// Actobserver.unsubscribe(fn);// Assertassert.strictEqual(observer.observers.length, 0);

o retorno de chamada deve corresponder à mesma função que está na lista. Se houver uma correspondência, o método de cancelamento de inscrição o removerá da lista. Observação o teste usa a referência da função para adicioná-la e removê-la.

o método de transmissão

para chamar todos os Eventos do:

broadcast(data) { this.observers.forEach((subscriber) => subscriber(data));}

isso itera através da lista de eventos observados e executa todos os retornos de chamada. Com isso, você obtém o relacionamento necessário um para muitos com os eventos inscritos. Você passa no parâmetro data que torna os dados de retorno de chamada vinculados.

ES6 torna o código mais eficaz com uma função de seta. Observe a função (subscriber) => subscriber(data) que faz a maior parte do trabalho. Esta função de seta de uma linha se beneficia dessa sintaxe ES6 curta. Esta é uma melhoria definitiva na linguagem de programação JavaScript.

para testar este método de transmissão, Faça:

// Arrangeconst observer = new EventObserver();let subscriberHasBeenCalled = false;const fn = (data) => subscriberHasBeenCalled = data;observer.subscribe(fn);// Actobserver.broadcast(true);// Assertassert(subscriberHasBeenCalled);

Use let em vez de const para que possamos alterar o valor da variável. Isso torna a variável mutável, o que me permite reatribuir seu valor dentro do retorno de chamada. Usar um let em seu código envia um sinal para outros programadores de que a variável está mudando em algum momento. Isso adiciona legibilidade e clareza ao seu código JavaScript.

este teste me dá a confiança necessária para garantir que o observador esteja funcionando como eu esperava. Com TDD, é tudo sobre a construção de código reutilizável em javascript simples. Existem benefícios em escrever código testável em JavaScript simples. Teste Tudo e mantenha o que é bom para reutilização de código.

com isso, desenvolvemos o EventObserver. A questão é: o que você pode construir com isso?

The Observer Pattern in Action: A Blog Word Count Demo

For the demo, time to put in place a blog post where it keeps the word count for you. Cada pressionamento de tecla que você inserir como entrada será sincronizado pelo padrão de design do observador. Pense nisso como entrada de texto livre onde cada evento dispara uma atualização para onde você precisa ir.

para obter uma contagem de palavras da entrada de texto livre, pode-se fazer:

const getWordCount = (text) => text ? text.trim().split(/\s+/).length : 0;

feito! Há muita coisa acontecendo nesta função pura aparentemente simples, então que tal um teste de unidade humilde? Dessa forma, fica claro o que eu pretendia que isso fizesse:

// Arrangeconst blogPost = 'This is a blog \n\n post with a word count. ';// Actconst count = getWordCount(blogPost);// Assertassert.strictEqual(count, 9);

observe a string de entrada um tanto maluca dentro blogPost. Pretendo que esta função cubra o maior número possível de casos extremos. Enquanto isso me der uma contagem de palavras adequada, estamos indo, de fato, na direção certa.

como uma nota lateral, Este é o verdadeiro poder do TDD. Pode-se iterar nesta implementação e cobrir o maior número possível de casos de uso. O teste de unidade informa como espero que isso se comporte. Se o comportamento tiver uma falha, por qualquer motivo, é fácil iterar e ajustá-lo. Com o teste, há evidências suficientes deixadas para qualquer outra pessoa fazer alterações.

tempo para conectar esses componentes reutilizáveis ao DOM. Esta é a parte em que você pode usar JavaScript simples e soldá-lo diretamente no navegador.

uma maneira de fazer isso seria ter o seguinte HTML na página:

<textarea placeholder="Enter your blog post..." class="blogPost"></textarea>

seguido por este JavaScript:

const wordCountElement = document.createElement('p');wordCountElement.className = 'wordCount';wordCountElement.innerHTML = 'Word Count: <strong>0</strong>';document.body.appendChild(wordCountElement);const blogObserver = new EventObserver();blogObserver.subscribe((text) => { const blogCount = document.getElementById('blogWordCount'); blogCount.textContent = getWordCount(text);});const blogPost = document.getElementById('blogPost');blogPost.addEventListener('keyup', () => blogObserver.broadcast(blogPost.value));

pegue todo o seu código reutilizável e coloque no lugar o padrão de design do observador. Isso rastreará as alterações na área de texto e fornecerá uma contagem de palavras logo abaixo dela. Estou usando o body.appendChild() na API DOM para adicionar este novo elemento. Em seguida, anexando os ouvintes do evento para trazê-lo à vida.

nota com funções de seta é possível conectar eventos de uma linha. Na verdade, você transmite mudanças orientadas a eventos para todos os assinantes com isso. O () => blogObserver.broadcast() faz a maior parte do trabalho aqui. Ele até passa as alterações mais recentes para a área de texto diretamente na função de retorno de chamada. Sim, o script do lado do cliente é super legal.

nenhuma demonstração está completa sem uma que você pode tocar e ajustar, abaixo está o CodePen:

veja a caneta o padrão Observer por SitePoint (@SitePoint) no CodePen.

agora, eu não chamaria esse recurso completo. É apenas um ponto de partida do padrão de design do observador. A questão em minha mente é: até onde você está disposto a ir?

olhando para o futuro

cabe a você levar essa ideia ainda mais longe. Existem muitas maneiras de usar o padrão de design do observador para criar novos recursos.

você pode melhorar a demonstração com:

  • Outro componente que conta o número de parágrafos
  • Outro componente que mostra uma pré-visualização de texto digitado
  • Melhorar a visualização com suporte a markdown, por exemplo,

Estas são apenas algumas idéias que você pode fazer para penetrar mais profundamente sobre isso. Os aprimoramentos acima desafiarão suas costeletas de programação.

conclusão

o padrão de design do observador pode ajudá-lo a resolver problemas do mundo real em JavaScript. Isso resolve o problema perene de manter um monte de elementos sincronizados com os mesmos dados. Como costuma acontecer, quando o navegador dispara eventos específicos. Tenho certeza de que a maioria de vocês já se deparou com esse problema e correu para Ferramentas e dependências de terceiros.

este padrão de design prepara você para ir tão longe quanto sua imaginação está disposta a ir. Na programação, você abstrai a solução em um padrão e cria um código reutilizável. Não há limite para o quão longe isso o levará.

espero que você veja o quanto, com um pouco de disciplina e esforço, você pode fazer em JavaScript simples. Os novos recursos no idioma, como o ES6, ajudam você a escrever algum código sucinto que seja reutilizável.

este artigo foi revisado por Giulio Mainardi. Obrigado a todos os revisores do SitePoint por tornar o conteúdo do SitePoint o melhor que pode ser!

Deixe uma resposta

O seu endereço de email não será publicado.