¿Qué es la Inyección de Dependencias (DI) en C# y por qué es necesaria?
\n\nLa Inyección de Dependencias (Dependency Injection) es un patrón de diseño que permite que el código sea más flexible, comprobable y mantenible. En lugar de que una clase cree por sí misma los objetos de los que depende, estos objetos se "inyectan" desde el exterior. Es uno de los principios clave en los que se basa el desarrollo moderno con C# y .NET.
\n\nImagine que está escribiendo una aplicación para enviar notificaciones por correo electrónico. Si su clase NotificationService crea directamente una instancia de SmtpClient, al cambiar de servidor de correo o necesitar usar otro método de envío (por ejemplo, a través de una API), tendrá que reescribir el código. DI resuelve este problema.
El problema sin Inyección de Dependencias
\n\nConsideremos un ejemplo típico de código deficiente, donde la dependencia se crea dentro de la clase:
\n\npublic class EmailService\n{\n private SmtpClient _smtpClient;\n\n public EmailService()\n {\n // Vinculación rígida a una implementación específica\n _smtpClient = new SmtpClient("smtp.gmail.com", 587);\n }\n\n public void SendEmail(string to, string subject, string body)\n {\n // Lógica de envío\n }\n}\n\npublic class NotificationService\n{\n private EmailService _emailService;\n\n public NotificationService()\n {\n _emailService = new EmailService(); // Acoplamiento rígido\n }\n\n public void NotifyUser(string email)\n {\n _emailService.SendEmail(email, "Notificación", "¡Hola!");\n }\n}\n\nDesventajas de este enfoque:
\n- \n
- Alto acoplamiento (tight coupling): Las clases
NotificationServiceyEmailServiceestán fuertemente vinculadas entre sí. \n - Dificultad para realizar pruebas: Es imposible sustituir
EmailServicepor un objeto mock durante las pruebas unitarias. \n - Dificultad de ampliación: Si necesita agregar notificaciones por SMS, tendrá que modificar la lógica dentro de
NotificationService. \n
Implementación de la Inyección de Dependencias en C#
\n\nDI resuelve estos problemas utilizando tres enfoques principales: inyección a través del constructor, a través de una propiedad y a través de un método. El más común y recomendado es la inyección a través del constructor (Constructor Injection).
\n\nPaso 1. Crear la interfaz
\nPrimero, definamos una abstracción (interfaz) que describa el contrato para enviar mensajes:
\n\npublic interface IMessageSender\n{\n void Send(string to, string message);\n}\n\nPaso 2. Implementar servicios concretos
\nAhora creemos varias implementaciones de esta interfaz:
\n\npublic class EmailSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Enviando email a {to}: {message}");\n }\n}\n\npublic class SmsSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Enviando SMS a {to}: {message}");\n }\n}\n\nPaso 3. Aplicar DI a través del constructor
\nAhora la clase NotificationService no crea las dependencias por sí misma, sino que las recibe a través del constructor:
public class NotificationService\n{\n private readonly IMessageSender _messageSender;\n\n // Inyección de dependencia a través del constructor\n public NotificationService(IMessageSender messageSender)\n {\n _messageSender = messageSender;\n }\n\n public void Notify(string userContact, string message)\n {\n _messageSender.Send(userContact, message);\n }\n}\n\nAhora podemos intercambiar fácilmente las implementaciones:
\n\n// Usamos Email\nvar emailSender = new EmailSender();\nvar emailNotifier = new NotificationService(emailSender);\nemailNotifier.Notify("user@example.com", "¡Hola por email!");\n\n// Usamos SMS\nvar sms