Aprendendo a modularizar: Object Literal Pattern

Photo by Kaboompics .com from Pexels

Um dos primeiros projetos que desenvolvi foi um sistema web de gerenciamento de documentos, durante o curso técnico, 5 anos atrás. Quanto ao JavaScript, lembro que o código todo ficou em 1 arquivo, que totalizava quase 4000 linhas. Nele haviam funções que faziam todo tipo de coisa: interação com o DOM, comunicação com o servidor, cálculos matemáticos, etc. Não era fácil dar manutenção nesse código, pois ele era longo, tinha muitas funções que dependiam umas das outras e havia código duplicado. Na época eu não sabia descrever o problema exatamente, mas hoje posso resumir tudo em poucas palavras: faltava modularidade.

Um código modular está dividido em partes, cada qual com funcionalidades específicas. Essas partes podem se comunicar, mas devem ser independentes, visto que as modificações em uma devem afetar o mínimo possível as demais. No contexto de um sistema informatizado, poderíamos aplicar os conceitos de programação modular separando as telas desses sistema em diferentes classes ou estruturas. Há várias formas de fazer isso, mas o exemplo que eu irei mostrar aqui segue a ideia do Object Literal Pattern, conforme apresentado nesse excelente vídeo do canal LearnCode Academy.

Tomemos como exemplo o site de uma empresa qualquer, que possua uma página inicial (home) com informações gerais e uma segunda página para exibir seu catálogo de produtos.

var telaHome = (function () { 
    //código relativo à tela
})();

var telaProdutos = (function () { 
    //código relativo à tela
})();

Qual é o sentido de representar as tela como variáveis? E pra quê tantos parênteses? Na verdade, criamos acima 2 funções auto-executáveis. Isso significa que, assim que o JavaScript da página for carregado, estas funções estarão disponíveis e poderão ser chamadas. E pra quê vamos usá-las? Assim como classes em um projeto Java contém métodos e atributos relacionados a elas, estas variáveis irão conter outras variáveis e funções relacionadas ao funcionamento das telas. Em vez de jogarmos todas as funções misturadas no mesmo arquivo, criamos uma estrutura para encapsulá-las.

var telaHome = (function () { 
    function mostraMsg() {
        console.log("Bem vindo ao site!");
    }
})();

var telaProdutos = (function () { 
    var produtos = ["Tênis XYZ Azul", "Sapato ABC Preto", "Tênis FGH Cinza"];
    
    function mostrarProdutos() {
        for (produto of produtos) { 
            console.log(produto);     
        } 
    }

    function limparLista() {     
        produtos = []; 
    }
})();

Quando criamos as funções dentro dessas estruturas, estas permanecem “invisíveis” para o resto do código. Comparando com o Java, é como se elas fossem métodos privados. Se quiséssemos chamar uma função de telaProdutos em telaHome (ou em qualquer outra tela), isso não seria possível. Porém, em muitos casos esse comportamento é desejável, e para conseguirmos fazer isso basta colocar as funções da tela como retorno de sua função pai (a própria tela). Esse retorno é feito em forma de um objeto com chave (nome pelo qual a função pode ser chamada) e valor (a referência da função em si).

var telaProdutos = (function () { 
    var produtos = ["Tênis XYZ Azul", "Sapato ABC Preto", "Tênis FGH Cinza"];
    
    function mostrarProdutos() {
        for (produto of produtos) { 
            console.log(produto);     
        } 
    }

    function limparLista() {     
        produtos = []; 
    }

    return {
        mostrar:mostrarProdutos,
        limpar:limparLista
    }
})();

A partir disso, fica simples chamar em qualquer lugar do código as funções encapsuladas. Novamente comparando com o Java, é como se tivéssemos mudado a visibilidade do método para “público”.

telaProdutos.mostrar();

Mas, e se eu quiser manter alguma função com visibilidade privada? Basta não colocá-la na lista de retorno.

var telaProdutos = (function () { 
    var produtos = ["Tênis XYZ Azul", "Sapato ABC Preto", "Tênis FGH Cinza"];
    function mostrarProdutos() {
        for (produto of produtos) { 
            console.log(produto);     
        } 
    }

    //não está no retorno
    function limparLista() {     
        produtos = []; 
    }

    return {
        mostrar:mostrarProdutos
    }
})();

Considerações finais: os conceitos de programação modular e padrões como o Object Literal Pattern podem nos ajudar a criar código mais organizado, legível e fácil de manter. Fazer a estruturação pode ser confuso no início, mas a longo prazo um design modular e componentizado facilita a manutenção de qualquer aplicação.

Fontes: