Introducción: ¿Qué es la Inyección de Dependencias y para qué sirve?
\n\nImagine que está armando una computadora. En lugar de comprar un monitor todo en uno ya hecho, usted elige por separado el procesador, la tarjeta de video, la memoria RAM y los conecta a la placa base. Si desea actualizar la tarjeta de video, no tendrá que cambiar toda la computadora, simplemente reemplazará un componente. La Inyección de Dependencias (DI) en programación funciona bajo el mismo principio.
\n\nLa Inyección de Dependencias (Dependency Injection) es un patrón de diseño en el cual un objeto recibe sus dependencias desde el exterior, en lugar de crearlas por sí mismo. En el contexto de JavaScript y Node.js, esto significa que una clase o función no crea instancias de otras clases internamente, sino que las recibe a través del constructor o de parámetros.
\n\nEste enfoque es parte del principio de Inversión de Dependencias (Dependency Inversion Principle) de SOLID y hace que el código sea más flexible, comprobable y mantenible. En este artículo, analizaremos la DI con ejemplos prácticos para desarrolladores principiantes.
\n\nEl problema: acoplamiento rígido del código
\n\nConsideremos una situación típica en una aplicación Node.js. Supongamos que tenemos un servicio para enviar notificaciones por correo electrónico:
\n\n// Sin Inyección de Dependencias\nclass EmailService {\n sendEmail(to, subject, body) {\n console.log(`Enviando correo a ${to}: ${subject}`);\n // lógica real de envío\n }\n}\n\nclass UserService {\n constructor() {\n this.emailService = new EmailService(); // ❌ Acoplamiento rígido\n }\n\n registerUser(email) {\n // Lógica de registro de usuario\n this.emailService.sendEmail(email, '¡Bienvenido!', '...');\n }\n}\n\nconst userService = new UserService();\nuserService.registerUser('user@example.com');\n\n\n¿Cuál es el problema? La clase UserService está rígidamente vinculada a una implementación específica de EmailService. Si desea:
- \n
- Reemplazar el envío de correos por el envío de SMS \n
- Añadir registro (logging) de todos los mensajes enviados \n
- Probar
UserServicesin enviar correos reales \n
Tendrá que reescribir el código de UserService. Esto viola el principio de abierto/cerrado (Open/Closed Principle).
Solución: inyección de dependencias a través del constructor
\n\nLa primera y más sencilla forma de inyección de dependencias es pasarlas a través del constructor de la clase. Reescribamos nuestro ejemplo:
\n\n// Con Inyección de Dependencias\nclass EmailService {\n sendEmail(to, subject, body) {\n console.log(`Enviando correo a ${to}: ${subject}`);\n }\n}\n\nclass SmsService {\n sendSms(phone, message) {\n console.log(`Enviando SMS a ${phone}: ${message}`);\n }\n}\n\nclass UserService {\n constructor(notificationService) {\n this.notificationService = notificationService; // ✅ Inyección de dependencia\n }\n\n registerUser(contact, isPhone = false) {\n // Lógica de registro\n if (isPhone) {\n this.notificationService.sendSms(contact, '¡Bienvenido!');\n } else {\n this.notificationService.sendEmail(contact, '¡Bienvenido!', '...');\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\nAhora UserService no sabe qué servicio específico se está utilizando — si correo o SMS. Simplemente llama al método sendEmail o sendSms del objeto que se le ha pasado. Esto se llama inversión de dependencias: el módulo de alto nivel no depende del módulo de bajo nivel, ambos dependen de una abstracción.
Ventajas de este enfoque:
\n- \n
- Flexibilidad: es fácil reempla