¿Qué es la Inyección de Dependencias en Java y para qué sirve?
Imagina que estás construyendo una casa. Necesitas ladrillos, cemento y herramientas. Si cada vez tienes que extraer la arcilla, cocer los ladrillos y preparar el cemento tú mismo, la construcción duraría una eternidad. Es mucho más fácil cuando los materiales ya están listos y entregados en la obra. Así funciona exactamente la Inyección de Dependencias (DI) en Java: es un patrón de diseño en el que un objeto recibe sus dependencias ya preparadas desde el exterior, en lugar de crearlas por sí mismo.
En palabras simples: la inyección de dependencias (Dependency Injection) es cuando un objeto le pasa a otro objeto todas las dependencias que necesita. En lugar de que la clase cree sus propias dependencias, las recibe a través del constructor, un setter o una interfaz.
¿Por qué es tan importante la Inyección de Dependencias?
- Reduce el acoplamiento del código: Las clases dejan de depender rígidamente de implementaciones concretas.
- Facilita las pruebas: Puedes reemplazar fácilmente objetos reales por mocks (objetos simulados) en pruebas unitarias.
- Aumenta la reutilización: Una misma clase se puede usar en diferentes contextos.
- Mejora la legibilidad: El código se vuelve más claro y las dependencias son explícitas.
El problema sin Inyección de Dependencias: el antipatrón "Acoplamiento rígido"
Veamos un ejemplo típico de código sin usar DI. Supongamos que tenemos una clase EmailService que envía correos, y una clase NotificationService que usa EmailService.
// Clase sin DI — mal ejemplopublic class EmailService { public void sendEmail(String message) { System.out.println("Enviando email: " + message); }}
public class NotificationService { private EmailService emailService;
public NotificationService() { // Problema: creamos la dependencia directamente dentro de la clase this.emailService = new EmailService(); }
public void sendNotification(String message) { emailService.sendEmail(message); }}¿Cuál es el problema de este código?
- Acoplamiento rígido:
NotificationServiceestá firmemente atado aEmailService. Si quieres enviar SMS en lugar de email, tendrás que reescribir la clase. - Dificultad para probar: No puedes reemplazar
EmailServicepor un simulacro de prueba (mock). - Violación del principio de responsabilidad única: La clase
NotificationServiceno solo se encarga de las notificaciones, sino también de crear el servicio de envío.
Cómo funciona la Inyección de Dependencias: tres formas de inyectar
En Java existen tres formas principales de implementar la inyección de dependencias. Veamos cada una con ejemplos de código.
1. Inyección por constructor (Constructor Injection)
Esta es la forma más común y recomendada. Las dependencias se pasan a través de los parámetros del constructor.
// Interfaz para flexibilidadpublic interface MessageService { void sendMessage(String message);}
// Implementación para emailpublic class EmailService implements MessageService { @Override public void sendMessage(String message) { System.out.println("Email enviado: " + message); }}
// Implementación para SMSpublic class SmsService implements MessageService { @Override public void sendMessage(String message) { System.out.println("SMS enviado: " + message); }}
// Clase con DI por constructorpublic class NotificationService { private final MessageService messageService;
// Inyección de dependencia por constructor public NotificationService(MessageService messageService) { this.messageService = messageSe