Menu
Coddy logo textTech

Fundamentos de Injeção de Dependência

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy. Lição 46 de 70.

Injeção de Dependência (DI) é uma técnica em que uma classe recebe suas dependências de fora, em vez de criá-las por conta própria. Isso torna o código mais flexível, testável e fracamente acoplado.

Sem DI, uma classe cria suas próprias dependências, o que dificulta alterá-la ou testá-la:

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

Com DI, as dependências são passadas pelo construtor:

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");
}

Agora, OrderService não sabe nem se importa se está usando e-mail, SMS ou um remetente simulado para testes. A dependência é “injetada” quando o objeto é criado:

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

Esse padrão segue o princípio de depender de abstrações (interface) em vez de implementações concretas. Ele permite substituir implementações sem modificar a class que as utiliza, o que é essencial para testes unitários e para manter arquiteturas flexíveis.

challenge icon

Desafio

Fácil

Vamos criar um sistema de registro que demonstra o poder da injeção de dependência. Em vez de definir diretamente como os registros são gravados, você criará uma classe Logger flexível que pode trabalhar com qualquer destino de registro — console, simulação de arquivo ou qualquer outra coisa — sem conhecer os detalhes específicos.

Você organizará seu código em três arquivos:

  • ILogWriter.cs: Defina uma interface chamada ILogWriter no namespace Logging. Essa interface representa o contrato para qualquer destino de registro e deve ter um único método Write(string message) que retorna uma string descrevendo onde a mensagem foi gravada.
  • LogWriters.cs: Crie duas implementações concretas de ILogWriter no namespace Logging:
    • ConsoleLogWriter — seu método Write retorna Console: {message}
    • FileLogWriter — recebe um nome de arquivo por meio de seu construtor, e seu método Write retorna File[{filename}]: {message}
  • Logger.cs: Crie uma classe Logger no namespace Logging que receba sua dependência por meio de injeção pelo construtor. A classe Logger deve:
    • Aceitar um ILogWriter por meio de seu construtor e armazená-lo em um campo privado
    • Ter um método Log(string message) que delega a chamada ao gravador injetado e retorna o resultado
  • Program.cs: Demonstre a injeção de dependência criando dois registradores diferentes — um com ConsoleLogWriter e outro com FileLogWriter. Mostre como a mesma classe Logger se comporta de maneira diferente com base na dependência injetada.

Você receberá duas entradas:

  • O nome do arquivo para o registrador de arquivo
  • A mensagem a ser registrada

Crie um Logger com um ConsoleLogWriter e registre a mensagem. Em seguida, crie outro Logger com um FileLogWriter (usando o nome do arquivo da entrada) e registre a mesma mensagem. Imprima cada resultado em sua própria linha.

Por exemplo, se as entradas forem app.log e System started, a saída deverá ser:

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

Observe como a classe Logger nunca muda — ela funciona com qualquer implementação de ILogWriter. Essa é a essência da injeção de dependência: o Logger depende de uma abstração (a interface), e a implementação concreta é "injetada" externamente. Você poderia facilmente adicionar um DatabaseLogWriter ou CloudLogWriter mais tarde, sem modificar a classe Logger!

Experimente você mesmo

using System;
using Logging;

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

        // TODO: Criar um Logger com um ConsoleLogWriter e registrar a mensagem
        // Imprimir o resultado

        // TODO: Criar outro Logger com um FileLogWriter (usando o filename) e registrar a mesma mensagem
        // Imprimir o resultado
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de C# online