Padrões de Projeto Avançados em JavaScript: da Teoria à Prática
\n\nPadrões de projeto são soluções comprovadas para problemas típicos no desenvolvimento de software. Para o desenvolvedor JavaScript que trabalha com Node.js, compreender padrões avançados abre caminho para a criação de sistemas escaláveis, flexíveis e fáceis de manter. Neste artigo, analisaremos quatro padrões-chave: Observer, Proxy, Chain of Responsibility e Strategy. Cada um será ilustrado com um exemplo prático em Node.js.
\n\n1. Padrão Observer (Observador): Arquitetura Baseada em Eventos no Node.js
\n\nObserver é um dos padrões comportamentais fundamentais. Ele define uma dependência "um para muitos" entre objetos: quando um objeto (sujeito) altera seu estado, todos os objetos dependentes (observadores) são automaticamente notificados e atualizados. No Node.js, esse padrão está na base do módulo EventEmitter.
Exemplo: sistema de notificações para uma loja online
\nImagine que temos um servidor que gerencia pedidos. Ao alterar o status de um pedido, é necessário notificar vários subsistemas: registro de logs, envio de e-mail e atualização de análises.
\n\nconst EventEmitter = require('events');\n\n// Sujeito (Subject)\nclass OrderManager extends EventEmitter {\n constructor() {\n super();\n this.orders = [];\n }\n\n createOrder(orderId, items) {\n const order = { orderId, items, status: 'created', createdAt: new Date() };\n this.orders.push(order);\n this.emit('order:created', order);\n return order;\n }\n\n updateStatus(orderId, newStatus) {\n const order = this.orders.find(o => o.orderId === orderId);\n if (order) {\n order.status = newStatus;\n this.emit('order:statusChanged', order);\n }\n }\n}\n\n// Observadores (Observers)\nfunction logObserver(order) {\n console.log(`[LOG] Pedido ${order.orderId}: status alterado para ${order.status} às ${new Date().toISOString()}`);\n}\n\nfunction emailObserver(order) {\n console.log(`[EMAIL] Enviando notificação para o pedido ${order.orderId} com status ${order.status}`);\n}\n\nfunction analyticsObserver(order) {\n console.log(`[ANALYTICS] Registrando evento: pedido ${order.orderId} -> ${order.status}`);\n}\n\n// Uso\nconst manager = new OrderManager();\nmanager.on('order:created', logObserver);\nmanager.on('order:statusChanged', logObserver);\nmanager.on('order:statusChanged', emailObserver);\nmanager.on('order:statusChanged', analyticsObserver);\n\nmanager.createOrder('ORD-001', ['Book', 'Pen']);\nmanager.updateStatus('ORD-001', 'shipped');\n\n\nEssa abordagem permite adicionar ou remover observadores sem alterar o código do sujeito, tornando o sistema flexível e facilmente extensível.
\n\n2. Padrão Proxy (Substituto): Controle de Acesso e Cache
\n\nProxy fornece um objeto substituto que gerencia o acesso a outro objeto. Em JavaScript, esse padrão é implementado através do objeto nativo Proxy, que permite interceptar e redefinir operações fundamentais (acesso a propriedades, chamada de funções, etc.).
Exemplo: proxy de cache para requisições de API
\nSuponha que temos um serviço que faz requisições caras a uma API externa. Queremos armazenar os resultados em cache para evitar chamadas repetidas.
\n\n// Serviço real (Real Subject)\nclass ExternalApiService {\n async fetchUser(id) {\n console.log(`Buscando usuário ${id} da API externa...`);\n // Simulação de atraso de rede\n await new Promise(resolve => setTimeout(resolve, 1000));\n return { id, name: `Usuário ${id}`, email: `user${id}@example.com` };\n }\n}\n\n// Proxy (Proxy)\nclass CachingProxy {\n constructor(realService) {\n this.realService = realService;\n this.cache = new M