Design Patterns em Javascript — A série

Uma aventura no mundo dos design patterns.

Nessa série de posts vou falar um pouco sobre Design Patterns em Javascript. A ideia aqui é que possamos discutir, trocar conhecimento sobre o assunto e para que eu possa documentar meu conhecimento para ele não ficar esquecido em um limbo no meu cérebro. Também é uma forma de motivação… enfim. Estou usando como guia o livro Mastering JavaScript Design Patterns — Third Edition.

Design Patterns?

Em 1977, Christopher Alexander, Sara Ishikawa e Murray Silverstein escreveram um livro sobre Design Patterns no planejamento urbano chamado: A Pattern Language: Towns. Buildings, Construction.

No livro, um padrão é descrito assim:

Cada padrão descreve um problema que ocorre várias vezes no nosso ambiente e quando você descreve a solução central para aquele problema de certa forma você pode usar essa solução um milhão de vezes sem nunca fazer da mesma forma duas vezes.

- Christopher Alexander

Mesmo que não seja sobre código, esse livro foi a inspiração de alguns livros de computação como Design Patterns: Elements of Reusable Object-Oriented Software.

O propósito de um Design Pattern não é te instruir a criar seu software e sim em te dar uma orientação para resolver um problema comum. Ele pode não ser exatamente a solução para o problema que você procura mas ele deve pelo menos lhe dar um orientação de como resolver seu problema de uma forma mais fácil.

A ideia do uso de Design Patterns em um projeto Javascript é de que não podemos mais colocar uma pilha de códigos em um script.js e rezar para que funcione. Também não podemos confiar em bibliotecas como jQuery para nos salvar. Bibliotecas apenas proveem funcionalidades adicionais e nada contribuem com a estrutura de uma aplicação. O mundo real muda com uma frequência muito rápida e qualquer aplicação que não conseguir se adaptar a essa frequência tende a ser esquecida. Design Patterns proveem alguma orientação em construir aplicações adaptáveis e que se adequam as mudanças nos negócios.

Antes de irmos direto aos Design Patterns em si precisamos entender um pouco do básico e esse básico inclui: Isolamento de código, modularização de código e orientação a objetos. Estes dois últimos veremos em um próximo post.


Intimide o seu código! Isole-o!

Se duas pessoas vão para uma mesma festa com a mesma roupa pega mal, não? Todo mundo para e fica olhando os dois gêmeos. Um dos dois ficará triste pelo mal entendido e ocorrerá um conflito.

O mesmo acontece no mundo da programação. A isolação de código evita dores de cabeça na hora de programação entre equipes ou até mesmo dentro de uma equipe ainda mais quando usamos bibliotecas de terceiros. Quantas vezes já não aconteceu aquele conflito com nome de variável e sabe-se lá de onde veio essa variável?

Abaixo veremos algumas dicas que podem te auxiliar na isolação de código.

Evite variáveis globais

Conforme vamos usando ferramentas de terceiros os problemas de conflito vão surgindo. Algumas bibliotecas como o jQuery tem a função “noconflict” que evita este tipo de problema com outras libs existentes no projeto. A princípio, você deve ter na cabeça que para evitar esse tipo de problema é recomendado que você nunca exiba uma variável global fora da sua biblioteca.

Namespaces

Um namespace é um delimitador abstrato que dá um contexto para o código existente dentro dele. Uma forma efetiva de criar um namespace é exportar uma palavra que nos representa como a empresa que criou aquilo, evitando conflitos com bibliotecas de terceiros. Exemplo:

ga: Google Analytics

fb: Facebook

yui: Yahoo User Interface

$: jQuery

Como você pode ver, um nome pequeno basta porém também é fácil de ser sobrescrito. Procure usar o nome da sua empresa como as grandes fazem para reduzir a expressão ao mínimo mas manter entendível de forma que seja de fácil entendimento.

Exemplo de como criar um namespace para a empresa Coca-Cola:

var cokePkt = cokePkt || {};
cokePkt.greeting = {
  hi: function hi() {
    console.log('hi');
  },
  hello: function hello() {
    console.log('hello');
  }
};

// Se você estiver no navegador, o cokePkt é appendado no Window.
// Se você estiver no Node.js, o cokePkt é appendado no Object Global.
// Para executar, basta digitar no console:
// cokePkt.greeting.hi();

Exemplo de namespace

Se você executar esse trecho de código no console do seu navegador, você conseguirá acessar a função hi e a função hello do namespace cokePkt.

A primeira linha é importante porque caso já exista algum namespace como o mesmo nome do nosso, o código apenas é incluído nesse namespace existente. Além de tudo namespaces são bons para organização dos nossos pacotes.


IIFE — Immediate Invoked Function Expression

Usar uma IIFE para criar um namespace é uma boa prática. Esse objeto será appendado ao objeto global do nosso código (se você estiver no Node será o Object Global e se você estiver no browser será o Window). Esta abordagem também nos ajuda a evitar o conflito com as variáveis globais permitindo uma melhor organização do código e dando a você o poder de expor apenas o que for parte da API pública, mantendo a implementação privada.

Outra boa prática é sempre adicionar o 'use strict' na função principal para evitar que poluamos o ambiente global “sem querer”.

Execute o seguinte código no console do navegador:

(function(namespace){
  'use strict';
  function getName() {
    return 'Lucas';
  }

  namespace.greeting = {
    hi: function hi() {
      console.log('hi ' + getName());
    },
    hello: function hello() {
      console.log('hello ' + getName());
    }
  };
}(this.cokePkt || (this.cokePkt = {})));

// Tente executar a função hi() e hello() que colocamos dentro
// do objeto greeting que está dentro do nosso namespace:
cokePkt.greeting.hi(); //hi Lucas
cokePkt.greeting.hello(); //hello Lucas

// tente executar a funcão getName():
cokePkt.greeting.getName(); //Uncaught TypeError: cokePkt.greeting.getName is not a function

Isso acontece porque exportamos apenas o que está dentro do objeto greeting que por sua vez está dentro da nossa IIFE. A função getName() só existiu dentro do contexto da IIFE. No momento de execução foi criado um escopo local dentro da IIFE e ela só existiu ali. Dessa forma, podemos escolher o que queremos expor.

Por hoje é isso! Nos próximos capítulos veremos um pouco sobre modularização de código e orientação a objetos.

Comentários