JavaScript e cavalheiros: por que você precisa de padrões de projeto (e como eles simplificarão sua vida)
\n\nVocê já abriu seu projeto antigo em JavaScript e pensou: "Quem escreveu isso? Isso é código espaguete!"? Se sim — bem-vindo ao clube. Mais cedo ou mais tarde, todo desenvolvedor se depara com uma situação em que o código se transforma em caos. E é aí que os padrões de projeto em JavaScript vêm em socorro — receitas comprovadas para tarefas típicas.
\n\nMas vamos combinar desde já: padrões não são uma bala de prata. Não é mágica que tornará seu código perfeito da noite para o dia. São ferramentas. E qualquer ferramenta só é boa quando você sabe quando e por quê aplicá-la. É exatamente sobre isso que vamos falar.
\n\nO que são padrões de projeto em palavras simples?
\n\nImagine que você é um arquiteto. Você não inventa uma nova forma de roda ou de vão de porta toda vez. Você usa soluções comprovadas: arcos, colunas, paredes estruturais. Padrões de projeto em JS são esses mesmos "projetos arquitetônicos", só que para código.
\n\nEles resolvem três problemas principais:
\n- \n
- Reutilização — não precisa reinventar a roda; \n
- Legibilidade — outro desenvolvedor entenderá sua lógica imediatamente; \n
- Escalabilidade — o código é fácil de estender sem risco de quebrar tudo. \n
Neste artigo, vamos analisar os quatro padrões mais populares que realmente serão úteis no trabalho do dia a dia. Sem enrolação — apenas código e prática.
\n\n1. Singleton: quando você precisa de apenas um
\n\nExistem situações em que deve existir exatamente uma instância de uma classe no aplicativo. Por exemplo:
\n- \n
- Armazenamento global de estado (como o Redux store); \n
- Logger para registrar erros; \n
- Conexão com banco de dados ou API. \n
O padrão Singleton garante que, não importa quantas vezes você tente criar um objeto — você obterá o mesmo.
\n\nExemplo: Contador de visitas
\n\nclass Counter {\n constructor() {\n if (Counter.instance) {\n return Counter.instance;\n }\n this.count = 0;\n Counter.instance = this;\n }\n\n increment() {\n this.count++;\n }\n\n getCount() {\n return this.count;\n }\n}\n\n// Uso\nconst counter1 = new Counter();\nconst counter2 = new Counter();\n\ncounter1.increment();\ncounter1.increment();\n\nconsole.log(counter2.getCount()); // 2 — porque é o mesmo objeto\nconsole.log(counter1 === counter2); // true\n\n\nQuando usar: quando for crítico ter um ponto único de acesso a um recurso. Mas tenha cuidado — um efeito colateral frequente do Singleton em JS é o estado global, que é difícil de testar. Não abuse.
\n\n2. Module: encapsulamento inteligente
\n\nAntes do surgimento dos ES6 Modules, os desenvolvedores usavam o padrão Module para criar métodos privados e públicos. Mesmo agora, com import/export, essa abordagem continua útil para organizar a lógica dentro de um único arquivo.
\n\nO segredo está nos closures. Criamos uma função que retorna um objeto com métodos, enquanto as variáveis internas permanecem inacessíveis externamente.
\n\nExemplo: Carrinho de compras de e-commerce
\n\nconst ShoppingCart = (function() {\n // Variáveis privadas\n let items = [];\n \n // Método privado\n function calculateTotal() {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }\n\n // API pública\n return {\n addItem(product) {\n const existing = items.find(item => item.id === product.id);\n if (existing) {\n existing.quantity++;\n } else {\n items.push({ ...product, quantity: 1 });\n }\n },\n removeItem(productId) {\n items = items.filter(item => item.id !== productId);\n },\n getTotal() {\n return calculateTotal();\n },\n clear() {\n items = [];\n }\n };\n}