.NET

7 ago, 2018

Entendendo inversão de controle e injeção de dependências nativas no .NET Core

Publicidade

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!