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 por sí misma. Esto hace que el código sea más flexible, comprobable y débilmente acoplado.

Sin DI, una clase crea sus propias dependencias, lo que dificulta cambiarlas o probarlas:

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 las 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

Construyamos un sistema de registro que demuestre el poder de la inyección de dependencias. En lugar de codificar directamente cómo se escriben los registros, crearás una clase flexible Logger que pueda trabajar 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 que describa 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 mediante 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 mediante la inyección en el constructor. Logger debe:
    • Aceptar un ILogWriter mediante 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 registradores 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 registrador de archivos
  • El mensaje que se registrará

Crea un Logger con un ConsoleLogWriter y registra el mensaje. Después 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: Logger depende de una abstracción (la interfaz), y la implementación concreta se "inyecta" desde fuera. ¡Más adelante podrías añadir fácilmente un DatabaseLogWriter o un CloudLogWriter sin modificar en absoluto la clase Logger!

Pruébalo tú mismo

using System;
using Logging;

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

        // TODO: Crea un Logger con un ConsoleLogWriter y registra el mensaje
        // Imprime el resultado

        // TODO: Crea otro Logger con un FileLogWriter (usando el filename) y registra el mismo mensaje
        // Imprime 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

Practica por tu cuenta: Compilador de C# online