Introdução: O que é Injeção de Dependência e para que serve?
\n\nImagine que você está montando um computador. Em vez de comprar um monobloco pronto, você escolhe separadamente o processador, a placa de vídeo, a memória RAM e os conecta à placa-mãe. Se quiser atualizar a placa de vídeo, não precisará trocar o computador inteiro — basta substituir um componente. Injeção de Dependência (DI) na programação funciona pelo mesmo princípio.
\n\nInjeção de Dependência (Dependency Injection) é um padrão de projeto no qual um objeto recebe suas dependências externamente, em vez de criá-las por conta própria. No contexto de JavaScript e Node.js, isso significa que uma classe ou função não cria instâncias de outras classes internamente, mas as recebe através do construtor ou parâmetros.
\n\nEssa abordagem faz parte do princípio da Inversão de Dependências (Dependency Inversion Principle) do SOLID e torna o código mais flexível, testável e sustentável. Neste artigo, vamos explorar a DI com exemplos práticos para desenvolvedores iniciantes.
\n\nO problema: acoplamento rígido do código
\n\nVamos considerar uma situação típica em uma aplicação Node.js. Suponha que temos um serviço para envio de notificações por e-mail:
\n\n// Sem Injeção de Dependência\nclass EmailService {\n sendEmail(to, subject, body) {\n console.log(`Enviando e-mail para ${to}: ${subject}`);\n // lógica real de envio\n }\n}\n\nclass UserService {\n constructor() {\n this.emailService = new EmailService(); // ❌ Acoplamento rígido\n }\n\n registerUser(email) {\n // Lógica de registro do usuário\n this.emailService.sendEmail(email, 'Bem-vindo!', '...');\n }\n}\n\nconst userService = new UserService();\nuserService.registerUser('user@example.com');\n\n\nQual é o problema? A classe UserService está rigidamente acoplada à implementação específica de EmailService. Se você quiser:
- \n
- Substituir o envio de e-mail pelo envio de SMS \n
- Adicionar registro (log) de todas as mensagens enviadas \n
- Testar o
UserServicesem realmente enviar e-mails \n
Você terá que reescrever o código de UserService. Isso viola o princípio aberto/fechado (Open/Closed Principle).
Solução: injeção de dependências através do construtor
\n\nA primeira e mais simples forma de injeção de dependências é passá-las através do construtor da classe. Vamos reescrever nosso exemplo:
\n\n// Com Injeção de Dependência\nclass EmailService {\n sendEmail(to, subject, body) {\n console.log(`Enviando e-mail para ${to}: ${subject}`);\n }\n}\n\nclass SmsService {\n sendSms(phone, message) {\n console.log(`Enviando SMS para ${phone}: ${message}`);\n }\n}\n\nclass UserService {\n constructor(notificationService) {\n this.notificationService = notificationService; // ✅ Injeção de dependência\n }\n\n registerUser(contact, isPhone = false) {\n // Lógica de registro\n if (isPhone) {\n this.notificationService.sendSms(contact, 'Bem-vindo!');\n } else {\n this.notificationService.sendEmail(contact, 'Bem-vindo!', '...');\n }\n }\n}\n\n// Uso\nconst emailService = new EmailService();\nconst userServiceWithEmail = new UserService(emailService);\nuserServiceWithEmail.registerUser('user@example.com');\n\nconst smsService = new SmsService();\nconst userServiceWithSms = new UserService(smsService);\nuserServiceWithSms.registerUser('+1234567890', true);\n\n\nAgora, UserService não sabe qual serviço específico está sendo usado — e-mail ou SMS. Ele simplesmente chama o método sendEmail ou sendSms do objeto recebido. Isso é chamado de inversão de dependências: o módulo de alto nível não depende do módulo de baixo nível; ambos dependem de uma abstração.
Vantagens dessa abordagem:
\n- \n
- Flexibilidade: fácil de substituir