DEV Community

Sadel Fortunato
Sadel Fortunato

Posted on

Dia 6: 🧩 Principio de Inversión de Dependencias (DIP) en C#

Principio de Inversión de Dependencias (DIP)
Definición

El Principio de Inversión de Dependencias establece que:

“Los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deben depender de abstracciones.”
“Las abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones.”
— Robert C. Martin (Uncle Bob)

En términos simples:

No dependas de clases concretas.

Depende de interfaces o abstracciones.

Inyecta las dependencias desde afuera.

Invierte la dirección de las dependencias.

Analogía del Enchufe
Sin el Principio (acoplamiento directo)

Imagina una laptop cuyo cable de corriente está soldado internamente:

Si el cable se daña, debe reemplazarse toda la laptop.

No permite usar diferentes voltajes.

Es difícil de adaptar para otros países.

Con el Principio (desacoplamiento)

Ahora imagina una laptop con un puerto estándar:

Permite conectar diferentes cables.

Cambiar el cable no afecta a la laptop.

Puede funcionar en cualquier país usando un adaptador adecuado.

Conclusión:
El puerto representa la abstracción y los cables son las implementaciones. La laptop no depende de un cable específico, sino de un estándar.

El Problema: Dependencia Directa
Ejemplo
// Clase de bajo nivel (detalle concreto)

public class EmailService
{
    public void EnviarEmail(string mensaje)
    {
        Console.WriteLine($"Enviando email: {mensaje}");
    }
}
Enter fullscreen mode Exit fullscreen mode

// Clase de alto nivel dependiente de una clase concreta

public class NotificacionService
{
    private EmailService _emailService = new EmailService();

    public void Notificar(string mensaje)
    {
        _emailService.EnviarEmail(mensaje);
    }
}
Enter fullscreen mode Exit fullscreen mode

// Uso del sistema

var notificador = new NotificacionService();
notificador.Notificar("Hola mundo"); // Solo puede enviar emails
Enter fullscreen mode Exit fullscreen mode

Problemas

Acoplamiento fuerte: NotificacionService depende directamente de EmailService.

Difícil de extender: No puede usarse SMS u otro canal sin modificar la clase.

Difícil de testear: No se pueden simular dependencias fácilmente.

Viola el Principio Abierto/Cerrado: No está cerrada a modificaciones.

La Solución: Inversión de Dependencias
Paso 1: Crear una Abstracción

public interface INotificacion
{
    void Enviar(string mensaje);
}

Enter fullscreen mode Exit fullscreen mode

Paso 2: Implementar las Clases Concretas

public class EmailService : INotificacion
{
    public void Enviar(string mensaje)
    {
        Console.WriteLine($"Email enviado: {mensaje}");
    }
}

public class SMSService : INotificacion
{
    public void Enviar(string mensaje)
    {
        Console.WriteLine($"SMS enviado: {mensaje}");
    }
}

public class WhatsAppService : INotificacion
{
    public void Enviar(string mensaje)
    {
        Console.WriteLine($"WhatsApp enviado: {mensaje}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Paso 3: Inyectar la Dependencia

public class NotificacionService
{
    private readonly INotificacion _canal;

    // Inyección de dependencia por constructor
    public NotificacionService(INotificacion canal)
    {
        _canal = canal;
    }

    public void Notificar(string mensaje)
    {
        _canal.Enviar(mensaje);
    }
}
Enter fullscreen mode Exit fullscreen mode

Paso 4: Uso del Sistema

var notificadorEmail = new NotificacionService(new EmailService());
notificadorEmail.Notificar("Hola por Email");

var notificadorSMS = new NotificacionService(new SMSService());
notificadorSMS.Notificar("Hola por SMS");

var notificadorWhatsApp = new NotificacionService(new WhatsAppService());
notificadorWhatsApp.Notificar("Hola por WhatsApp");
Enter fullscreen mode Exit fullscreen mode

Ventaja principal:
El comportamiento puede cambiarse sin modificar la clase NotificacionService. Solo se inyecta una nueva implementación de la interfaz.

Tipos de Inyección de Dependencias

  1. Inyección por Constructor (recomendada)
public class ServicioUsuario
{
    private readonly IRepositorio _repositorio;

    public ServicioUsuario(IRepositorio repositorio)
    {
        _repositorio = repositorio ?? throw new ArgumentNullException();
    }
}
Enter fullscreen mode Exit fullscreen mode

Ventajas:

Las dependencias son obligatorias desde la creación del objeto.

Las propiedades pueden mantenerse inmutables (readonly).

Facilita la realización de pruebas unitarias (mocking).

Mejora la claridad del código al mostrar explícitamente qué necesita la clase.

Top comments (0)