Design Patterns no JavaScript – Module
Leia em 2 minutos
Design Patterns é um assunto recorrente de todas as linguagens. Vou começar uma nova série de artigos sobre eles aplicados no JavaScript. E este primeiro artigo irá falar sobre o pattern Module.
O Module Pattern é muito utilizado porque ele permite organizar melhor o código, sem expor variáveis globais de modo promíscuo. Como ainda não temos uma sintaxe de módulos do próprio JavaScript, usamos os módulos para garantir que o escopo de variáveis seja fechado, além de simular a privacidade de atributos e funções.
Este pattern pode envolver uma combinação de diversas técnicas como closures e funções auto-executáveis. A sintaxe é bastante característica e pode ser encontrada facilmente em diversas bibliotecas.
(function(){
// your code here
})();
No JavaScript, funções são apenas objetos. E, por esse motivo, pode ser atribuídas em variáveis. Nesse nosso caso, estamos pulando o passo de atribuição e executamos a função imediatamente após sua definição. Como definir uma função anônima e executá-la não é uma sintaxe válida, precisamos englobá-la com os parênteses (embora seja possível fazer algumas gambiarras para permitir isso).
function(){}(); // SyntaxError: Unexpected token (
+function(){}(); // Executes the function
-function(){}(); // Executes the function
Existe uma variação desta sintaxe que adiciona os parentêses de execução dentro dos parênteses da expressão.
(function(){
// your code here
}());
Como o JavaScript ainda não possui módulos nativamente (embora isso provavelvemente entre na versão ECMAScript 6, também chamada de Harmony), este pattern é muito utilizado para simular módulos. Isso só é possível por causa do escopo local de variáveis de uma função, que permite isolar tudo o que é feito dentro da função.
É muito comum, por exemplo, usarmos o Module Pattern para criar plugins do jQuery, pois passamos o objeto jQuery
como dependência e atribuímos a uma variável com um nome melhor, normalmente $
.
(function($){
// your plugin here
})(jQuery);
A ideia é bastante simples. Funções podem ser executadas e receber parâmetros. Nesse caso, estamos executando uma função anônima e passando o parâmetro jQuery
. Na função anônima podemos receber os parâmetros que foram passados durante a execução. Como não existe nenhuma obrigatoriedade de mantermos o mesmo nome, podemos atribuí-la à variável $
.
Como variáveis definidas em uma função tem escopo local, podemos usar o Module Pattern para simular privacidade de atributos.
var Counter = {
count: 0
, increment: function() {
return this.count += 1;
}
};
Counter.increment();
Counter.increment();
Counter.count; // returns 2
Counter.count = 100;
Counter.count; // returns 100
O grande problema dessa implementação é que é muito simples quebrar o encapsulamento e manipular o atributo Counter.count
diretamente. Em vez disso, podemos criar uma variável local que irá armazenar o contador e expor uma interface pública que retorna esse valor.
var Counter = (function(){
var count = 0;
return {
count: function() {
return count;
}
, increment: function() {
return count += 1;
}
};
})();
Note que ambas as funções referenciam a variável count
, que não pode ser acessada diretamente de fora da função auto-executável. Dessa forma, podemos encapsular todo o comportamento, sem termos que nos preocupar com modificações feitas sem ser pela função Counter.increment()
.
Essa técnica de retornar uma interface pública à partir de um módulo também é chamada de Revealing Module Pattern.
Finalizando
Enquanto ainda não temos módulos nativamente, o Module Pattern é bastante útil e ajuda bastante nesta tarefa. A implementação de módulos diretamente na linguagem vai ajudar bastante já que a sintaxe é muito mais simples e direta.
No próximo artigo iremos ver um pouco sobre o Singleton Pattern.