Um dos grandes problemas no desenvolvimento de software é a dependência entre classes que geramos durante o desenvolvimento. Classes são responsáveis por instanciar um objeto, delegar alguma tarefa e se encarregar de finalizar esse objeto. Existem diversas técnicas para mitigar esse problema, e uma das mais comuns é a injeção de dependências em conjunto com a inversão de controle.
O problema
public class ClasseA { // Nesse caso, estamos criando a dependência, a utilizando, e a descartando. // Assumindo assim, toda a responsabilidade. public void Metodo() { Dependencia dep = new Dependencia(); dep.FazAlgumaCoisa(); } }
Inversão de Controle
Esse padrão consiste em: deixar o “dono” do objeto passar a dependência para a nossa classe, e assim, tiramos essa responsabilidade e tratamos somente o que importa para nosso método.
Para implementarmos com as melhores práticas, devemos sempre trabalhar com interfaces, pois nos prendemos à um contrato, e não à uma implementação.
Nossa nova versão da classe:
public class ClasseA { private IDependencia _dep; // Agora, nós precisamos que quem chama o método, passe algum objeto que cumpre o contrato // e tiramos a responsabilidade de criação do objeto. public ClasseA(IDependencia dep) { _dep = dep; } public void Metodo() { _dep.FazAlgumaCoisa(); } }
É nesse contexto que a injeção de dependências entra em ação; nós delegamos para um módulo ou classe, o trabalho de saber qual é a implementação que desejamos passar para essa classe que depende dessa interface.
No ambiente .NET existem diversas bibliotecas para cumprir essa tarefa: Ninject, SimpleInjector, entre outros.
.NET Core e a injeção de dependências nativas
Quando estamos trabalhando com .NET Core, não há a necessidade de trabalharmos com essas bibliotecas, pois já temos uma implementação nativa de injeção de dependências!
Primeiramente, vamos supor que temos uma API que depende da nossa interface:
[Route("api/[controller]")] public class ServicoController : Controller { private readonly IDependencia _dependencia; // Estamos seguindo o padrão da inversão de controle para recebermos a nossa dependência public ServicoController(IDependencia dep) { _dependencia = dep; } [HttpGet] public IActionResult Get() { return Ok(_dependencia.FazAlgumaCoisa()); } }
Agora, para habilitarmos a injeção dessa dependência de forma automática pelo .Net Core, vamos alterar nossa classe Startup.cs:
public class Startup { public void ConfigureServices(IServiceCollection services) { // Declaramos que quando alguma classe requisitar um IDependencia, devemos entregar um objeto do tipo Dependencia services.AddTransient<IDependencia, Dependencia>(); // Demais linhas que já vem por default com o projeto omitidas } public void Configure(IApplicationBuilder app, IHostingEnviroment env) { ... } }
Dessa maneira, ao chamarmos nossa API, receberemos um objeto do tipo Dependência como parâmetro. Note que usamos o método AddTransient, que cria um objeto novo a cada solicitação, mas existem outros métodos que criam o objeto em determinadas situações:
- AddTransient: cria um objeto a cada solicitação; se três objetos solicitarem a dependência, três instâncias são criadas.
- AddScoped: cria um objeto por request, se três objetos da mesma requisição solicitarem, os três objetos estarão lidando com a mesma instância, mas em uma segunda requisição, o objeto criado será outro.
- AddSingleton: cria somente um objeto, e este é compartilhado para todos os clientes que solicitarem a dependência.
Este é somente um artigo inicial sobre o tema, que possui muito conteúdo a ser explorado. A utilização de cada método depende da necessidade da sua aplicação e do contexto a ser lidado.
Espero que esse artigo tenha aberto um caminho para você explorar esse padrão e melhorar cada dia mais a qualidade das aplicações criadas.
Um forte abraço, e até logo!