Padrões de DI – Parte 1: Introdução

Inicio com este post uma série sobre padrões de Dependency Injection ou, daqui em diante, simplesmente “DI”.

Este primeiro consiste em uma rápida introdução sobre DI, antes de seguirmos com os padrões de fato.

1. O QUE É DI

DI é uma técnica que tem por finalidade o desenvolvimento de software com baixo acoplamento e, portanto, mais fácil de ser mantido.

Pensar em DI é pensar com carinho onde empregamos o uso do famoso “new”, de forma que possamos minimizar o acoplamento entre as classes do software, tornando-o mais flexível.

2. PRINCIPAIS VANTAGENS

DI é o meio e não o fim. Portanto, as vantagens obtidas estão no resultado, ou seja, no código de baixo acoplamento. São elas:

Late binding: capacidade de substituir comportamento do software por meio de configuração, ou seja, sem a necessidade de recompilar o código.

Extensibilidade: habilidade de mudar o código com o mínimo de alterações possíveis. Lembram do Princípio do Aberto/Fechado?

Manutenibilidade: facilidade de manter o código, graças a uma definição bem clara de responsabilidades. Lembram do Princípio da Responsabilidade Única?

Testabilidade: no sentido de a aplicação ser capaz de ser testada por meio de testes de unidade.

3. O QUE DI COBRE

3.1. OBJECT COMPOSITION

Inicialmente o foco de DI estava unicamente na composição de objetos (Object Composition), isto é, na capacidade de construir aplicações pela composição de módulos/componentes.

Isso acontece quando quebramos as classes em responsabilidades bem definidas e removemos o controle das dependências PARA FORA da classe-consumidora. Em outras palavras, quem USA uma dependência, não a CRIA.

Retirando esse “peso” de criar a dependência da classe-consumidora, ganhamos mais flexibilidade, podendo usar (ou injetar) diferentes implementações.

Vejamos um pequeno exemplo:

// uma classe-consumidora altamente acoplada
// à sua dependência
public class UmServicoQualquer
{
   public void Executa()
   {
      var repositorio = new SqlRepositorio();
      var obj = repositorio.ObterPor(id: 5);
      //.......
   }
}

// mesma classe, agora fazendo uso de DI
public class UmServicoQualquer
{
   private readonly IRepositorio repositorio;

   public UmServicoQualquer(IRepositorio repositorio)
   {
      this.repositorio = repositorio;
   }

   public void Executa()
   {
      var obj = this.repositorio.ObterPor(id: 5);
      //........
   }
}

Observem que, na primeira implementação, o consumidor cria explicitamente a dependência (new SqlRepositorio()), ficando acoplado a uma implementação concreta do repositório.

Já na última implementação, a dependência é injetada no consumidor, deixando-o livre da responsabilidade de criação e totalmente alheio à implementação da dependência.

Para alcançarmos o tão precioso baixo acoplamento, é imprescindível um dos conceitos mais antigos de orientação a objetos: programar para interfaces, não para implementações. Exatamente o que foi feito no exemplo, onde o consumidor dependente de uma abstração, a interface IRepositorio.

Além da composição de objetos, hoje em dia outros dois conceitos são aspectos importantes em DI: Interception e Object Lifetime.

3.2. INTERCEPTION

Ao aplicarmos DI, como visto no item anterior, ganhamos a capacidade de interceptar o momento de injeção da dependência e “envelopá-la” em outra classe, acrescentando comportamento à dependência sem modificá-la.

Parece familiar? Sim. Interception nada mais é do que uma aplicação do design pattern Decorator. Usando Interception, podemos implementar cross-cutting concerns como log e controle de acesso, entre outros.

3.3. OBJECT LIFETIME

Outro aspecto que ganhamos ao inverter as dependências é a habilidade de controlarmos quando novas instâncias são criadas e quando saem do escopo, ou seja,  o tempo de vida do objeto (Object Lifetime).

Por exemplo, podemos decidir quando uma mesma instância de uma dependência deverá ser injetada em todo consumidor (Singleton lifetime) ou quando uma nova instância é criada toda vez que a dependência for solicitada (Transient lifetime).

4. TIPOS DE DEPENDÊNCIAS

Agora estamos prontos para sair injetando tudo que imaginarmos, certo? Muita calma nessa hora. Existem dependências estáveis e instáveis (ou voláteis). O foco de DI está na segunda categoria.

São dependências voláteis aquelas que possuem pelo menos uma das características abaixo:

– São dependências externas, ou seja, que estão fora do processo da aplicação, como bancos de dados, message queues, web services e o sistema de arquivos do SO.

– Possuem comportamento não-determinístico, como o famoso DateTime.Now ou geração de números aleatórios.

– Necessitam ser abstraídas por possuírem diferentes comportamentos, que precisam ser alternados em tempo de execução (ex.: strategies).

Em geral, estaremos injetando “fazedores de coisas”e que normalmente deixam a aplicação mais “desplugável” (*), ou seja, mais fácil de ter seus módulos substituídos ou usados (inclusive por testes) em isolamento.

(*) conceitualmente, chamamos de seam esta dependência (abstração) que serve de ponto de contato entre uma parte e outra da aplicação.

5. RELAÇÃO ENTRE DI E O PRINCÍPIO DA INVERSÃO DE DEPENDÊNCIA (DIP)

Falamos anteriormente sobre programar para interfaces, conceito tratado no DIP. Portanto, o Princípio da Inversão de Dependência é o princípio que é base para a prática de DI.

6. CONCLUSÃO

Este artigo, o primeiro da série “Padrões de DI”, apresentou o conceito de Dependency Injection, dando uma visão geral do que é DI e seus 3 aspectos: Object Composition, Interception e Object Lifetime.

Embora os dois últimos aspectos sejam também de grande importância, esta série focará exclusivamente em Object Composition, que é a base de DI, trazendo padrões e conceitos que lidam sobre ONDE compor as dependências, QUANDO resolvê-las e COMO injetá-las.

» Continuar para [Parte 2: Composition Root]

6 comentários em “Padrões de DI – Parte 1: Introdução

Adicione o seu

  1. Aprendendo muito com seus artigos, já uso alguns desses padrões que você apresenta em seu blog porém sem saber que uso haha, a nomeclatura não sou muito bom nas sopas de letrinhas, mas sei o que deixa meu codigo facil de manter! Logo seus artigos me ajuda a identificar os nomes doa padrões que uso!

    O layout do seu blog é otimo para leitura em celular 🙂

    Curtir

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Blog no WordPress.com.

Acima ↑