A resposta mais votada atualmente a uma pergunta muito recente afirma que
Os contêineres DI são um padrão de "software corporativo", usado quando o gráfico de objetos é muito grande e complexo. Suspeito que 95% dos aplicativos não exijam isso.
o que é algo que discordo totalmente. Talvez eu tenha entendido errado a terminologia, mas para mim o framework DI significa apenas "algo conectando meus objetos". Estou esquecendo de algo?
Estou usando o Guice mesmo para projetos muito pequenos (como 10 classes) para simplificá- los. Claro, é um arquivo JAR de 400 kB, mas não é com isso que eu me importo. Para um projeto pequeno, quase nunca preciso de nenhuma configuração e a única "sobrecarga" é adicionar a @Inject
anotação.
Então, eu realmente me pergunto, que complexidade adicional as estruturas de DI causam?
Atualização abordando as respostas
Em um projeto de 82 classes, eu tenho
- 32
@Inject
anotações - 15
@Singleton
e 1@ProvidedBy
anotações - 4 fornecedores (todos os quais eu precisaria também sem DI, pois são minhas fábricas)
- 1 Módulo contendo uma única linha
- 0 linhas XML !!!
Isso é tudo. Claro, é um projeto pequeno, mas exatamente esse foi o meu ponto. O trabalho adicional foi mais algumas palavras do que linhas .
Estou usando apenas injeção de construtor para obter objetos imutáveis "descontraídos". Sempre que uma nova dependência aparece, adiciono um campo final, deixo que o RequiredArgsConstructor da Lombok cuide da declaração e Guice cuide de chamá-la corretamente.
fonte
Respostas:
Existem alguns compromissos a serem feitos ao usar estruturas de DI, tanto quanto posso ver.
O mais preocupante para mim é que o código do seu aplicativo geralmente se espalha entre uma linguagem de programação principal (como Java) e o código de configuração XML / JSON ( é o código). Isso significa que, no caso de problemas com seu aplicativo, você precisa procurar em dois lugares. Geralmente, o código de configuração não é facilmente relacionado ao código principal do aplicativo.
É claro que a configuração também está fora dos limites do que o compilador (e geralmente o IDE) pode verificar para você, o que significa que é muito mais fácil cometer erros nessa configuração do que se você estivesse escrevendo uma classe principal que tratava da fiação. De fato, isso significa que os problemas de fiação passam de problemas de tempo de compilação para problemas de tempo de execução.
Além de dividir o aplicativo, o uso de DI Frameworks também significa que você usa
@Inject
ou similar no seu código, em vez de técnicas tradicionais de DI (injeção de interface CTOR em Java / C ++, injeção de modelo em C ++). A desvantagem disso é que você deve usar uma estrutura DI com o aplicativo. Uma alternativa seria projetar seu aplicativo sem esperar uma estrutura DI e permitir que a estrutura DI reutilizasse a injeção tradicional CTOR / setter de suas classes.A desvantagem do mecanismo de injeção tradicional ocorre quando uma classe complexa e adequadamente encapsuladora exige várias outras injeções relativamente complexas no momento do CTOR. Isso geralmente é resolvido com a incorporação de a
Builder
, mas pode ser uma dor.EDIT :
Os rapazes abaixo mencionaram que
Guice
não precisa de uma configuração XML / JSON separada. Minha resposta realmente se aplica ao meu próprio uso do Spring.fonte
Eu acho que a principal desvantagem de usar um contêiner de IoC é toda a mágica que ele faz nos bastidores. (Isso também é um problema com ORMs, outra ferramenta mágica comum.) Depurar problemas de IoC não é divertido, porque fica mais difícil ver como e quando as dependências estão sendo cumpridas. Você não pode percorrer a resolução de dependência no depurador.
Os contêineres dificultam a configuração da "classe A com uma versão de uma dependência e da classe B com uma versão diferente" ou cenários semelhantes em que as abstrações são reutilizadas. Você pode acabar em situações em que cada classe tem sua própria interface, mesmo quando elas significam a mesma coisa. Isso o impede do poder de abstração de reutilizar interfaces.
Na minha opinião, a grande vitória que você obtém da IoC é o gerenciamento automático do estilo de vida. Você pode declarar componentes como singletons, singletons por solicitação, instâncias transitórias etc. - e o contêiner cuida de tudo isso para você. É consideravelmente mais difícil configurar isso usando
new
-expressions. Por outro lado, ainda é fácil causar vazamentos de recursos devido a estilos de vida incompatíveis.Alguns projetos usam a IoC para construir tudo no sistema - mesmo classes que representam dados comerciais - sem
new
palavras-chave. Eu acho que isso geralmente é um erro, porque incentiva você a escrever classes mal encapsuladas e muitas interfaces. Seu código se torna dependente da IoC. Também parece ser co-mórbido com o excesso de teste.Pessoalmente, geralmente prefiro ficar sem um contêiner. Em vez disso, componho objetos usando
new
-expressions encapsulated em uma raiz de composição. É fácil ver as dependências concretas de uma determinada classe e, quando troco um construtor, obtenho erros em tempo de compilação, os quais são grandes vitórias de manutenção. Também é fácil configurar instâncias separadas com componentes diferentes.Meu conselho, se você deseja usar um contêiner de IoC, é cumprir suas dependências no ponto de entrada do sistema e usá-lo apenas para suas dependências reais (repositórios e outros). E lembre-se de que você ainda pode usar a injeção de dependência e até manter a configuração em um único arquivo, sem usar um contêiner.
fonte
Alguns desenvolvedores argumentam que qualquer estrutura usada em seu código é um problema em potencial em uma data posterior, porque normalmente exige que você escreva seu código de uma certa maneira para interagir com ele corretamente, e isso pode causar problemas em uma data posterior. Acontece que essas formas entram em conflito com outros requisitos de design. Um exemplo dessa preocupação é expresso por Bob Martin aqui . Eu pessoalmente não concordo, mas posso entender o que ele disse.
fonte
Para resumir: Parece que a única complexidade vem do uso de métodos de configuração inadequados (sim, o XML não é seguro para tipos) e possivelmente também de estruturas que executam muito mais trabalho (o Spring gerencia o ciclo de vida do objeto, que vai muito além do DI).
Para um projeto simples, o DI fazendo o certo é extremamente útil:
Também é bastante inofensivo, pois tudo pode ser feito manualmente. É apenas a criação das instâncias, passando-as aos construtores para criar mais instâncias, e assim por diante. Chato e longo, mas simples.
Um serviço típico usando Guice e Lombok se parece com isso
Agora, adicionar uma dependência significa apenas adicionar um novo campo final privado . Não pode ser nulo (o Guice garante isso mesmo sem @NonNull ).
Testar significa criar suas próprias instâncias ou obtê-las do
Injector
(possivelmente configurado para retornar alguns stubs). Ambos são bem simples.fonte