C# - Injeção de dependência(DI) e Inversão de Controle(IoC)


 Neste artigo vamos rever os conceitos de injeção de depêndência (DI) e de inversão de controle(Ioc) no .NET Core usando a linguagem C#.

O que é a injeção de dependência(DI) ?

Pense bem, atualmente o ciclo de desenvolvimento de software é muito rápido e isso exige testes unitários e uma atualização constante. Acontece que ao realizar alterações no código do seu projeto você não deseja que efeitos colaterais, como erros inesperados afetem outras partes do seu código.

Para minimizar ao máximo esse problema a solução é criar aplicações modulares onde as dependências entre os módulos seja reduzida e você tenha um baixo acoplamento e uma alta coesão.

O recurso que vai te ajudar a alcançar esse objetivo é a injeção de dependência (DI).

Este recurso permite a injeção de dependências do lado de fora de uma classe de forma que a classe onde a dependência é injeta só precisa saber do contrato (definido em uma interface ou classe abstrata), e assim, a classe pode ser independente dos seus objetos.

Usando a DI em seu código, os testes unitários são facilitados, visto que apenas uma classe específica precisa ser testada, e as dependências podem ser substituídas por uma classe simulada que contém dados de teste.

A DI também permite substituir a funcionalidade padrão por recursos personalizados. A ASP.NET Core e o Entity Framework Core são fortemente baseados em injeção de dependência e usam centenas de contratos - por exemplo, para encontrar um controlador, para mapear uma solicitação HTTP para um controlador, para converter dados recebidos para um parâmetro, para mapear uma tabela de banco de dados para um tipo de entidade e assim por diante. Você pode facilmente substituir a funcionalidade personalizada usando uma implementação diferente.

Assim, a DI é o padrão básico do desenvolvimento ágil de software e de práticas contínuas de entrega de software.

Você não precisa usar um contêiner de injeção de dependência para aplicar a injeção de dependência, mas ele ajuda a gerenciar as suas dependências. Assim que você tiver uma lista crescente de serviços gerenciados pelo contêiner de injeção de dependência você pode ver suas vantagens em reduzir a complexidade do seu código.

Vamos então entender como a DI funciona e porque ela é importante.

Vamos começar com um exemplo simples que não usa a DI e depois transformá-lo para usar a DI.

Criando o projeto Console

Abra o VS 2017 Community e crie um projeto do tipo Console usando o .NET Core com o nome NetCore_DI.

1- Exemplo sem usar a Injeção de Dependência

Vamos começar um exemplo sem usar a injeção de dependência; mais tarde vamos mudá-lo para usar dependência
injeção. A implementação do serviço usado é definida na classe RecepcaoService que define o método Saudacao que retorna uma string.

    public class RecepcaoService
    {
        public string Saudacao(string nome) => $"Olá, {nome}";
    }

 

A classe Controller utiliza o serviço, e, no método Ola() criamos uma instância de RecepcaoService e invocamos o método Saudacao:

    public class Controller
    {
        public string Ola(string nome)
        {
            var service = new RecepcaoService();
            return service.Saudacao(nome);
        }
    }

No método Main da classe Program estamos instanciando a classe Controller e invocando o método OLa() escrevendo o resultado no console:

    class Program
    {
        static void Main(string[] args)
        {
            var controller = new Controller();
            string resultado = controller.Ola("Macoratti");
            Console.WriteLine(resultado);
            Console.ReadLine();
        }
    }

Executando o programa vemos que ele esta funcionando. E daí ????

Analisando melhor o código vemos que a classe Controller e a classe RecepcaoService estão fortemente acoplados. (O uso da palavra chave new é um indício de acoplamento)

Esse forte acoplamento torna difícil substituir a classe RecepcaoServcice na classe Controller usando uma implementação diferente.

O código simples usado no exemplo pode mascarar o problema mas em aplicativos reais geralmente o cenário é mais complexo; RecepcaoService poderia ter que acessar uma API usando requisições HTTP ou teria que acessar um banco de dados usando o Entity Framework Core, e mudar o código nestes cenários seria mais desafiador e complicado.

Além disso ao realizar os testes unitários para a classe Controller você teria que testar também a classe RecepcaoService, e o teste de unidade, como o próprio nome indica, deve ser usado para testar apenas a funcionalidade dos métodos de uma única classe sem usar outras dependências.

Embora existam maneiras de contornar os problemas citados a injeção de dependência é a melhor forma de fazer isso e a seguir veremos como alterar a implementação para usar a injeção de dependência.

2- Exemplo usando a Injeção de Dependência

Vamos tornar a classe Controller independente da implementação da classe RecepcaoService.

Podemos fazer isso criando uma interface IRecepcaoService que define a funcionalidade necessária pela classe Controller:

    public class IRecepcaoService
    {
        string Saudacao(string nome);
    }

Agora a nossa classe RecepcaoService implementa a interface IRecepcaoService :

    public class RecepcaoService : IRecepcaoService
    {
        public string Saudacao(string nome) => $"Olá, {nome}";
    }

Assim, a classe Controller agora precisa de uma referência a um objeto que implementa a interface IRecepcaoService, e, ela é injetada no construtor da classe Controller, atribuida a um campo privado e usada com um  método Ola():

    public class Controller
    {
        private readonly IRecepcaoService _recepcaoService;
        public Controller(IRecepcaoService recepcaoService)
        {
            _recepcaoService = recepcaoService ??
            throw new ArgumentNullException(nameof(recepcaoService));
        }
        public string Ola(string nome) => _recepcaoService.Saudacao(nome);
    }

Nesta implementação, a classe Controller, utiliza o princípio da inversão de controle (IoC) pois não instancia a classe RecepcaoService() como fazia anteriormente. Ao invès disso, o controle para definir a classe concreta usada na classe Controller é delegada para um agente externo, ou seja, o controle foi invertido.

Nota: A inversão de controle (IoC) também é conhecida como o princípio de Hollywood: não ligue para nós, nós chamamos você;

Agora, a classe Controller não tem uma dependência de uma implementação concreta, e, pode utilizar qualquer classe que implemente a interface IRecepcaoService. Esta classe precisa apenas implementar todos os membros da interface.

De onde virá a injeção da dependência ?

Ela vai precisar ser injetada a partir do exterior passando uma implementação concreta para o construtor da classes Controller.

Por esse motivo essa abordagem é chamada de injeção de construtor pois a interface é injetada no construtor.

Vamos alterar o código do método Main() da classe Program para passar uma implementação concreta da interface IRecepcaoService para o construtor da classe Controller. Aqui a dependência é injetada:

   class Program
    {
        static void Main(string[] args)
        {
            var controller = new Controller(new RecepcaoService());
            string resultado = controller.Ola("Macoratti");
            Console.WriteLine(resultado);
        }
    }

Creio que ficou claro o conceito da injeção da dependência e do princípio da inversão de controle.

No entanto nossa aplicação exemplo é muito simples e temos que injetar apenas uma única classe concreta que implementa uma interface e esta classe é instanciada ao mesmo tempo que instanciamos a classe Controller.

Ocorre que em aplicações reais temos que lidar com muitas interfaces e suas implementações e precisamos também compartilhar essas instãncias.

Para poder gerenciar todas as interfaces e suas implementações podemos usar um contêiner de injeção de dependência.

Na próxima parte do artigo vamos alterar nosso código para usar o contêiner de injeção de dependência nativa da .NET Core :  o contêiner Microsoft.Extensions.DependencyInjection.

Pegue o código projeto aqui:  NetCore_DI.zip

Louvai ao Senhor dos senhores; porque a sua benignidade dura para sempre.

Salmos 136:3

"Louvai ao Senhor dos senhores; porque a sua benignidade dura para sempre."
Salmos 136:3

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti