IOC / DI – Você está fazendo isso errado!
Publicado em: quarta-feira, 8 de jan de 2014
Categorias: Architecture

Olá, agora vou tocar em um assunto que parece uma ferida para muitos, mas considero bem básico. Infelizmente, acredito haja quem confunda estes conceitos, e por ter encontrado pela estrada alguns projetos com tais problemas relacionados à compreensão de IoC e DI, venho escrever esse post. É um tema tão cotidiano que hoje está disponível também no Macoratti*, portanto não vou explicar o conceito, mas me ater no que vejo de errado por aí.

Service Locators

A questão que é que vejo em muitos lugares, implementações de Service Locators e Factories como sendo consideradas implementações de IoC e/ou DI. Esse é um equívoco gigantesco e causa problemas não só estruturais, como o aumento da complexidade nas soluções, também afeta o design de grandes sistemas. É o tipo de problema que você leva meses para resolver, pois geralmente está proliferado em todo o código e não é difícil encontrarmos outros problemas graves nas modelagens, causados geralmente pela abordagem.

Vamos ao que interessa, vamos falar de algumas soluções que vejo no lugar de IoC / DI.

1) Service Locator é um padrão para resolução de dependências, mas não é um padrão para injeção das dependências.

Quando seu código chama um Service Locator para obter uma dependência, a classe em questão está assumindo o controle da execução, e está apenas delegando para um terceiro (o Service Locator) o papel de encontrar uma instância correta. Se houvesse injeção de dependência, sua classe não precisaria chamar um Service Locator, a dependência já estaria injetada e pronta para uso. Nesse caso, você não inverteu o controle, e sequer injetou a dependência. Isso significa que cada classe que precise de uma dependência, precisa saber construí-la ou solicitá-la, e é esse o problema em questão.

Essa abordagem geralmente funciona bem quando só há uma única implementação possível para cada dependência. Bastando haver duas ou mais implementações possíveis para um mesmo tipo, para que este padrão exija deformações no seu design. O problema fica pior quando você quer duas instâncias da mesma classe, configuradas de forma diferente.

Exemplo:

Contexto

Vou dar o exemplo de um Aluno que estuda. Definimos uma classe Aluno, que possui um método Estudar. Para incrementar o problema, vamos assumir que este método faça uso de um “local de estudo”, modelado como uma propriedade chamada LocalDeEstudo, do tipo ILocalDeEstudo, uma interface implementada por duas classes: Escrivaninha MesaDeJantar.

Imagine que o problema para resolver é representar dois alunos que estudam em locais diferentes, depois solicitar que ambos estudem. Para o exemplo teremos Aluno1, que estuda numa escrivaninha enquanto o segundo, chamado de Aluno2, estuda em uma Mesa de Jantar.

Problema

Como você preencheria cada instância com uma implementação diferente de ILocalDeEstudo?

Algumas soluções possíveis

Se você pensou em um IF no construtor, esqueça (e se mate, por favor).

Se você pensou em adicionar um argumento(string, int, enum) ao construtor para que este seja usado na escolha da implementação, esqueça.

Se você pensou na possibilidade da própria classe Aluno solicitar a um Service Locator uma instância que implemente ILocalDeEstudo, então o Service Locator terá um problema a resolver. Saber qual implementação de ILocalDeEstudo você precisa.

Se você pensou que o é responsabilidade de quem criou a instância da classe Aluno, definir explicitamente qual é a dependência necessária pelo aluno, você está no caminho certo, mas o S do SOLID, está pesando contra você.

Então inevitavelmente, usando Service Locators para esse problema fará com que este seja obrigado a compreender o contexto em que está sendo executado, para determinar a implementação, instância ou mesmo versão correta de um objeto para entregá-lo e assim você terá de poluir seu design para conseguir contextualizá-lo.

2) Factories

Factories e suas variações são muito interessantes, mas oferecem os mesmos problemas dos Service Locators.

Então, isso não é IoC

Você pode está pensando em diversas outras soluções para resolver o problema, até usar AOP para contextualizar o Factory ou o Service Locator, mas qualquer abordagem que exija intervenção na  classe que necessita da dependência, está gerando em algum nível conflito com princípios do SOLID. Toda a complexidade deste cenário existe porque não há inversão de controle, mas uma divisão de responsabilidades, entre a classe dependente e o Service Locator ou FactoryPara que haja de fato inversão de controle, é necessário delegar a construção das dependências, completamente. Tentar controlar o fluxo de execução é um erro grave.

Sugestão

Injeção de dependência é uma excelente solução em conjunto com IoC. Embora Factories possam fazer IoC, acredito que a gestão do código dos factories pode ser algo extremamente complexo e extenso. Para resolver essa complexidade, sugiro containers. A proximidade do IoC do DI é exatamente essa, a inversão de controle só é real, quando usando injeção de dependência. Mas cuidado, Injeção de Dependência, sem container, é traumático e algo extremamente e trabalhoso.

E lembre-se, somente o nível mais elevado do sua arquitetura deve solicitar instâncias ao seu container / Factory / Service Locator. No caso do ASP.NET, você pode configurar factories, eles podem usar um container para obter todo o grafo de dependências desde a UI até o nível mais baixo. No ASP.NET MVC também é possível, e é até mais simples, com mais exemplos. Windows Services, Consoles não possuem abstração e não há alternativa**, apenas codificar manualmente, mas lembre-se sempre, somente Entry Points devem solicitar instâncias (acho que essa dica soluciona a questão).

Grande abraço a todos.

* Macoratti é um excelente site, mas para quem está aprendendo. Que fique claro meu orgulho por ter feito parte dos primeiros leitores dos artigos relativos a .Net, isso em ~2003.

** Existem frameworks que ajudam a abstrair Windows Services, como TopShelf.

Leia o artigo do Martin Fowler ( http://martinfowler.com/articles/injection.html )

[update][2017-05-04]

Não se convenceu com exemplos acima? Ok, vou apresentar um novo exemplo:

Mail Services (v1)

Vamos supor que eu tenha uma interface IMailService, como abaixo:

public interface IMailService
{
    void SendMail(MailMessage messageToSend);
}

Premissas:

Agora vou assumir que meu sistema deve utilizar em 90% das ações um SMTP server próprio da empresa enquanto para 10% das necessidades de envio de email, utilize MailChimp ou algum serviço na nuvem parecido. Olhando para a implementação, assumo que tenho 2 implementações possíveis para envio de email: MailChimpMailService e SMTPMailService. Para dar vida ao exemplo, vamos supor que MailChimpMailService envie um HTTP POST para um endereço de api, enquanto SMTPMailService conecta a um servidor SMTP comum. Pronto, já temos o problema evidenciado.

Explicação:

Quem quer que consuma essa interface, não deve se preocupar com a forma como o email é enviado. Seja com o envio para uma API REST, uma conexão SMTP, o insert em diversas tabelas de um banco, ou mesmo o envio para um Message Broker. Quem consome essa interface, deve saber construir um email, para envio. Nada mais. E esse é o ponto chave que faz com que a abordagem escolhida pela Microsoft e hoje em dia por toda a comunidade .NET esteja errada. Falta algo mais.

Mail Services (v2)

Não está convencido, vou piorar. Pense que o MailChimp criou uma nova forma de enviar email, em vez de uma API REST, você simplesmente conectando ao Servidor SMTP deles. Então vc pode jogar a classe MailChimpMailService no lixo (feliz) e passar a usar apenas a classe SMTPMailService. Isso resolveria a questão? Não, pois ainda: meu sistema deve utilizar em 90% das ações um SMTP server próprio da empresa enquanto para 10% das necessidades de envio de email, utilize MailChimp ou algum serviço na nuvem parecido.

Então, a complexidade continua ali, onde não deveria existir. Por precisar usar mais de uma configuração para a resolução de uma dependência.

Até há soluções para isso, mas a forma como a Microsoft escolheu apresentar Injeção de Dependência, sem IoC, chega a ser criminosa. Mas isso é culpa do projeto Castle Windsor, e esse problema começa em 2003!

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou. 

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Eventos passados

novembro 2020
outubro 2020
setembro 2020
agosto 2020
Nenhum evento encontrado!
Carregar Mais

Publicidade

Assine

3 Comentários

  1. André

    Olá Luiz!
    Primeiramente, parabéns pelo post… e foi interessante que um post leva à outro e cheguei até este, legal!
    Mas, senti falta da solução concreta, seja pro exemplo do aluno, seja praquele do email. Conseguiria passar qual seria a solução ideal nesses casos?
    Grande abraço

    Responder
    • Luiz Carlos Faria

      André, reli o texto e a explicação está nele. É abstrato. Mas falar de código sem código é um “”músculo” que precisa ser estimulado, e uma intenção adjacente do texto é essa.
      De qualquer forma eu vou trabalhar em cima desse texto para tentar melhorar a explicação.

      Responder
  2. Rafael

    Estou estudando o assunto e excelente a sua reflexão, todavia não consegui chegar à solução do problema. Seria de grande ajuda se pudesse publicar em forma de código como o amigo já comentou.

    Responder

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.

Agenda & Eventos

outubro

novembro 2020

dezembro
DOM
SEG
TER
QUA
QUI
SEX
SAB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
Events for 1st novembro
Sem Eventos
Events for 2nd novembro
Sem Eventos
Events for 3rd novembro
Sem Eventos
Events for 4th novembro
Sem Eventos
Events for 5th novembro
Sem Eventos
Events for 6th novembro
Sem Eventos
Events for 7th novembro
Sem Eventos
Events for 8th novembro
Sem Eventos
Events for 9th novembro
Sem Eventos
Events for 10th novembro
Sem Eventos
Events for 11th novembro
Sem Eventos
Events for 12th novembro
Events for 13th novembro
Sem Eventos
Events for 14th novembro
Sem Eventos
Events for 15th novembro
Sem Eventos
Events for 16th novembro
Sem Eventos
Events for 17th novembro
Sem Eventos
Events for 18th novembro
Sem Eventos
Events for 19th novembro
Sem Eventos
Events for 20th novembro
Sem Eventos
Events for 21st novembro
Sem Eventos
Events for 22nd novembro
Sem Eventos
Events for 23rd novembro
Sem Eventos
Events for 24th novembro
Sem Eventos
Events for 25th novembro
Sem Eventos
Events for 26th novembro
Sem Eventos
Events for 27th novembro
Sem Eventos
Events for 28th novembro
Sem Eventos
Events for 29th novembro
Sem Eventos
Events for 30th novembro
Sem Eventos
Share This