Menu
Coddy logo textTech

Fundamentos de Inyección de dependencias

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 46 de 70.

Inyección de Dependencias (DI) es una técnica en la que una clase recibe sus dependencias desde el exterior en lugar de crearlas ella misma. Esto hace que el código sea más flexible, fácil de probar y con un acoplamiento débil.

Sin DI, una clase crea sus propias dependencias, lo que dificulta su modificación o prueba:

public class OrderService
{
    private EmailSender sender = new EmailSender();  // Fuertemente acoplado
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Con DI, las dependencias se pasan a través del constructor:

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Ahora OrderService no sabe ni le importa si está usando correo electrónico, SMS o un remitente simulado para pruebas. La dependencia se "inyecta" cuando se crea el objeto:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // Email: Pedido realizado

Este patrón sigue el principio de depender de abstracciones (interfaces) en lugar de implementaciones concretas. Permite intercambiar implementaciones sin modificar la clase que las utiliza, lo cual es esencial para las pruebas unitarias y para mantener arquitecturas flexibles.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de registro (logging) que demuestre el poder de la inyección de dependencias. En lugar de codificar de forma rígida cómo se escriben los registros, crearás una clase Logger flexible que pueda funcionar con cualquier destino de registro (consola, simulación de archivo o cualquier otra cosa) sin conocer los detalles específicos.

Organizarás tu código en tres archivos:

  • ILogWriter.cs: Define una interfaz llamada ILogWriter en el espacio de nombres Logging. Esta interfaz representa el contrato para cualquier destino de registro y debe tener un único método Write(string message) que devuelva una cadena describiendo dónde se escribió el mensaje.
  • LogWriters.cs: Crea dos implementaciones concretas de ILogWriter en el espacio de nombres Logging:
    • ConsoleLogWriter: su método Write devuelve "Console: " + message
    • FileLogWriter: recibe un nombre de archivo a través de su constructor y su método Write devuelve "File[" + filename + "]: " + message
  • Logger.cs: Crea una clase Logger en el espacio de nombres Logging que reciba su dependencia a través de la inyección por constructor. El Logger debe:
    • Aceptar un ILogWriter a través de su constructor y almacenarlo en un campo privado
    • Tener un método Log(string message) que delegue en el escritor inyectado y devuelva el resultado
  • Program.cs: Demuestra la inyección de dependencias creando dos loggers diferentes: uno con un ConsoleLogWriter y otro con un FileLogWriter. Muestra cómo la misma clase Logger se comporta de manera diferente según la dependencia que se inyecte.

Recibirás dos entradas:

  • El nombre de archivo para el logger de archivo
  • El mensaje a registrar

Crea un Logger con un ConsoleLogWriter y registra el mensaje. Luego crea otro Logger con un FileLogWriter (usando el nombre de archivo de la entrada) y registra el mismo mensaje. Imprime cada resultado en su propia línea.

Por ejemplo, si las entradas son app.log y System started, la salida debería ser:

Console: System started
File[app.log]: System started

Observa cómo la clase Logger nunca cambia: funciona con cualquier implementación de ILogWriter. Esta es la esencia de la inyección de dependencias: el Logger depende de una abstracción (la interfaz), y la implementación concreta se "inyecta" desde el exterior. ¡Podrías agregar fácilmente un DatabaseLogWriter o CloudLogWriter más tarde sin modificar la clase Logger en absoluto!

Hoja de referencia

La Inyección de Dependencias (DI) es una técnica en la que una clase recibe sus dependencias desde el exterior en lugar de crearlas ella misma, lo que hace que el código sea más flexible, testable y débilmente acoplado.

Sin DI (fuertemente acoplado):

public class OrderService
{
    private EmailSender sender = new EmailSender();  // Fuertemente acoplado
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Con DI (débilmente acoplado):

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Inyectando la dependencia:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // Email: Order placed

La DI sigue el principio de depender de abstracciones (interfaces) en lugar de implementaciones concretas, lo que permite intercambiar implementaciones sin modificar la clase que las utiliza.

Pruébalo tú mismo

using System;
using Logging;

class Program
{
    public static void Main(string[] args)
    {
        // Leer entradas
        string filename = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Crear un Logger con un ConsoleLogWriter y registrar el mensaje
        // Imprimir el resultado

        // TODO: Crear otro Logger con un FileLogWriter (usando el filename) y registrar el mismo mensaje
        // Imprimir el resultado
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos