O que é Dependency Injection (DI) em C# e por que é necessário?
\n\nDependency Injection (injeção de dependências) é um padrão de projeto que torna o código mais flexível, testável e sustentável. Em vez de a classe criar os objetos dos quais depende, esses objetos são "injetados" externamente. Este é um dos princípios-chave que fundamentam o desenvolvimento moderno em C# e .NET.
\n\nImagine que você está escrevendo um aplicativo para enviar notificações por e-mail. Se sua classe NotificationService criar diretamente uma instância de SmtpClient, ao trocar o servidor de e-mail ou precisar usar outro método de envio (por exemplo, via API), você terá que reescrever o código. A DI resolve esse problema.
O problema sem Dependency Injection
\n\nVamos considerar um exemplo típico de código ruim, onde a dependência é criada dentro da classe:
\n\npublic class EmailService\n{\n private SmtpClient _smtpClient;\n\n public EmailService()\n {\n // Acoplamento rígido a uma implementação 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 envio\n }\n}\n\npublic class NotificationService\n{\n private EmailService _emailService;\n\n public NotificationService()\n {\n _emailService = new EmailService(); // Acoplamento rígido\n }\n\n public void NotifyUser(string email)\n {\n _emailService.SendEmail(email, "Notificação", "Olá!");\n }\n}\n\nDesvantagens dessa abordagem:
\n- \n
- Alto acoplamento (tight coupling): As classes
NotificationServiceeEmailServiceestão rigidamente ligadas entre si. \n - Dificuldade de teste: É impossível substituir
EmailServicepor um objeto mock durante os testes unitários. \n - Dificuldade de extensão: Se for necessário adicionar notificações por SMS, será preciso alterar a lógica dentro de
NotificationService. \n
Implementação de Dependency Injection em C#
\n\nA DI resolve esses problemas usando três abordagens principais: injeção via construtor, via propriedade e via método. A mais comum e recomendada é a injeção via construtor (Constructor Injection).
\n\nPasso 1. Criando a interface
\nPrimeiro, definimos uma abstração (interface) que descreve o contrato para envio de mensagens:
\n\npublic interface IMessageSender\n{\n void Send(string to, string message);\n}\n\nPasso 2. Implementando serviços concretos
\nAgora, criamos algumas implementações dessa interface:
\n\npublic class EmailSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Enviando e-mail para {to}: {message}");\n }\n}\n\npublic class SmsSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Enviando SMS para {to}: {message}");\n }\n}\n\nPasso 3. Aplicando DI via construtor
\nAgora, a classe NotificationService não cria as dependências por conta própria, mas as recebe através do construtor:
public class NotificationService\n{\n private readonly IMessageSender _messageSender;\n\n // Injeção de dependência via construtor\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\nAgora podemos trocar facilmente as implementações:
\n\n// Usando Email\nvar emailSender = new EmailSender();\nvar emailNotifier = new NotificationService(emailSender);\nemailNotifier.Notify("user@example.com", "Olá via e-mail!");\n\n// Usando SMS\nvar sms