Criando objetos com Module Pattern

Neste post iremos falar um pouco a respeito de um Design Pattern que pode nos ajudar bastante na hora de definirmos um objeto em javascript. Esse pattern é o Module, com ele conseguimos definir o encapsulamento do nosso objeto, além de protegermos as variáveis não deixando-as expostas fora do contexto do objeto. Para usarmos esse pattern da maneira correta, antes é interessante entender alguns conceitos que estão por trás do mesmo.

O Module se apoia no uso de Closures, anonymous functionself-executing ou self-invoking functions do Javascript.

Anonymous function

Vamos começar falando sobre o anonymous function, a maneira como definimos uma função anonima no javascript segue a seguinte sintaxe:

var foo = function() {
  console.log("Calling foo");
};

Agora se chamarmos essa função:

foo();

Teremos a saída:

> Calling foo

Como toda função em javascript é um objeto, podemos atribuir sua definição a uma variável e depois chamá-la usando o mesmo nome.

Self-executing

Agora, e se quiséssemos que toda vez que esse código fosse interpretado pelo navegador o nosso método foo fosse chamado?.
Para que isso seja possível, recorremos ao uso da técnica self-executing, que resume-se em adicionar um parênteses no final da definição do método, forçando-o a ser invocado ao término de sua definição, veja o exemplo abaixo:

var foo = function() {
  console.log("Calling foo");
}();

Teremos como saída:

> Calling foo

Observe que não foi necessário chamar a função foo.

Closures

Closures são comumente usadas para fazer wrapper de objetos em javascript, com essa técnica criamos escopos dentro de nossas funções, a visibilidade de variáveis e funções representam uma certa hierarquia. Por conta dessa característica podemos criar variáveis e métodos “privados”.
Basicamente uma Closure é uma função dentro de outra função e que tem acesso as variáveis definidas pela função hospedeira mas que não expõem suas variáveis e funções internas para o escopo acima.
Parece confuso? Vamos ver alguns exemplos dos escopos de uma variável dentro de uma Closure para entendermos melhor tudo isso:

function outside() {
  var myName = "Rafael";
  console.log(myName);

  function inside() {
    var myName = "Anthony";
    console.log(myName);
  }

  inside();
}

Ao chamarmos a função outside.

outside();

Teremos a seguinte saída.

> Rafael
> Anthony

Como podemos notar, existem duas variáveis com o mesmo nome, ou seja, temos dois myName, no entanto existe uma hierarquia na definição. A variável dentro do escopo outside define a variável myName, e na função inside uma variável de mesmo nome é criada só que para o escopo interior. Portanto a função inside, consegue enxergar as variáveis definidas dentro de outside, mas como temos uma variável de mesmo nome no escopo inside ela é usada por padrão, o inverso não é possível.
Vejamos mais um exemplo:

function outside() {
  var myName = "Rafael";
  console.log(myName);

  function inside() {
    var myName = "Anthony";
    console.log(myName);
  }

  inside();

  console.log("My name is " + myName);
}

outside();

Saída:

> Rafael
> Anthony
> My name is Rafael

A função outside não consegue enxergar o escopo da função inside, portanto ela só conhece a variável myName definida por ela mesma.
Agora se não definirmos a variável myName dentro do escopo da função inside e imprimirmos a variável, podemos ver que myName definida dentro do escopo outside é acessível dentro do escopo inside.

function outside() {
  var myName = "Rafael";

  function inside() {
    console.log(myName);
  }

  inside();
}

outside();

Saída:

> Rafael

Já o inverso resulta em um erro de referência pois tentamos chamar uma variável indefinida.

function outside() {

  function inside() {
    var myName = "Rafael";
  }

  inside();

  console.log(myName);
}

outside();

Saída:

> ReferenceError: myName is not defined

Agora que já entendemos os conceitos de cada técnica por trás do Module Pattern, vamos dar uma olhada em algumas das inúmeras maneiras de criar um objeto em javascript:

Usando funções

function Client() {
  this.id = 123;
  this.name = "Rafael";
  this.active = true;
  this.getName = function() {
    return this.name;
  };
}

var client = new Client();
console.log(client.id);
console.log(client.getName());

Object Literals

var client = {
  id: 123,
  name: "Rafael",
  active: true,
  getName: function() {
    return this.name;
  }
};

console.log(client.id);
console.log(client.getName());

Singleton usando uma função

var client = new function() {
  this.id = 123;
  this.name = "Rafael";
  this.active = true;
  this.getName = function() {
    return this.name;
  };
};

console.log(client.id);
console.log(client.getName());

Usando prototype:

var Client = function(){};
Client.prototype.id = 123;
Client.prototype.name = "Rafael";
Client.prototype.active = true;
Client.prototype.getName = function() {
  return this.name;
};

var client = new Client();

console.log(client.id);
console.log(client.getName());

Ou então criando uma instância diretamente:

var client = new Object();
client.id = 123;
client.name = "Rafael";
client.active = true;
client.getName = function() {
  return client.name;
};

console.log(client.id);
console.log(client.getName());

Não vamos entrar nos detalhes e diferenças de cada implementação para não deixar o post extenso demais.

Vamos entender porque essas implementações não são as melhores abordagens.
Como podemos notar, em todos os exemplos a variável id, name e active estavam expostas permitindo um acesso externo através da instância ou simplesmente pelo nome de referência do objeto.
O Module Pattern nos ajudará a criar escopos “privados”, ou seja, vamos ter a oportunidade de impedir o acesso a variáveis diretamente, evitando assim que o nosso encapsulamento seja quebrado.
Muitas bibliotecas como o jQuery por exemplo, usam Module Pattern para determinar quais métodos devem ser expostos aos usuários e quais métodos devem permanecer em um contexto privado para uso interno de suas funções.

Então como é um objeto definido usando o Module Pattern? Vejamos:

var client = (function() {
  var id = 123,
      name = "Rafael",
      active = true;

  function getName() {
    return name;
  }

  return {
    getName: getName
  };
})();

console.log(client.id);
console.log(client.getName());

O modulo é definido usando um parênteses envolvendo a função principal, isso faz um Wrapper do objeto fechando o escopo interno.
As variáveis definidas dentro do modulo, ficam num contexto privado sendo acessadas somente pelas funções internas e nunca por alguém de fora.
Outro detalhe importante que devemos entender é a maneira como expomos uma variável ou um método para acesso externo.
Adicionando uma função e/ou variável dentro do return tornamos esses recursos públicos, permitimos que essa variável e/ou função estejam disponíveis para acesso externo.

Vejamos a saída desse código:

> undefined
> Rafael

Como a variável id não está dentro do return, ela não é visível no contexto externo, quem o chamar diretamente receberá um undefined como retorno.
Com isso em mente podemos criar métodos e/ou variáveis publicas e também privadas, vamos ver um exemplo mais completo:

var client = (function() {
  var id = 123,
      name = "Rafael",
      active = true;

  function getName() {
    return name;
  };

  function getInfo() {
    return "Id: " + id + ", Name: " 
        + name + ", active: " + active;
  };

  return {
    active: active,
    getName: getName
  };
})();

console.log(client.id);
console.log(client.active);
console.log(client.getName());
console.log(client.getInfo());

Saída:

> undefined
> true
> Rafael
> TypeError: undefined is not a function

Injeção de dependências

Podemos tirar inúmeras vantagens de adotar esse pattern, uma delas é a injeção de dependências. Através dessa técnica conseguimos passar para o nosso objeto um referência de um recurso do qual faremos uso.
Vamos imaginar que você precise fazer uso do jQuery dentro do seu objeto, se você já possuí o script em sua página, bastaria você fazer algo como:

var loginForm = (function() {

  function getFormReference() {
    return $("#signIn");
  }

  function validateForm() {
    var $form = getFormReference(),
        $user = $form.find('#user'),
        $password = $form.find('#user');

    return ($user.text() != '' && $password.text() != '');
  }

  function doTheAuthentication() {
    // ajax to authenticate user
    console.log('Authenticating the user...');
  }

  return {
    validate: validateForm,
    authenticate: doTheAuthentication
  };

})();

Repare que dentro do nosso método getFormReference(), fazemos uso de um seletor para encontrar o elemento de id signIn, essa abordagem funciona bem desde que você não possua outras bibliotecas de manipulação de DOM além do jQuery. Imagine que por algum motivo você precise usar além do jQuery a biblioteca Prototype.js. Como essas bibliotecas usam o $ como referência para criar os seletores, você precisaria de alguma forma dizer para o objeto loginForm que dentro do método getFormReference(), você quer usar o jQuery para fazer a seleção do elemento e não outra biblioteca.
Para resolver esse problema, podemos passar para o nosso objeto loginForm uma referência da biblioteca que queremos usar. A injeção dessa dependência é feita da seguinte maneira:

var loginForm = (function($) {

  function getFormReference() {
    return $("#signIn");
  }

  function validateForm() {
    var $form = getFormReference(),
        $user = $form.find('#user'),
        $password = $form.find('#user');

    return ($user.text() != '' && $password.text() != '');
  }

  function doTheAuthentication() {
    // ajax to authenticate user
    console.log('Authenticating the user...');
  }

  return {
    validate: validateForm,
    authenticate: doTheAuthentication
  };

})(jQuery.noConflict());

Agora dentro do nosso objeto a referência para $ é exatamente a biblioteca jQuery, pois passamos ela como parâmetro para o nosso objeto.

A maneira como usamos o objeto loginForm continua a mesma:

loginForm.validate();
loginForm.authenticate();

Saída:

> false
> Authenticating the user...

Conclusão

Existem inúmeros Patterns em Javascript que podem nos ajudar muito com problemas corriqueiros, esse é só um deles.
Usando essas técnicas tornarmos o nosso código mais coeso, seguro e de fácil manutenibilidade.

Dúvidas ou sugestões, favor deixar nos comentários, responderei assim que possível :)