Recentemente, trabalhei em um projeto Python em que realizamos injeção de dependência intensamente (porque precisamos que o aplicativo seja testável), mas não usamos nenhuma estrutura. Às vezes, era um pouco entediante conectar todas as dependências manualmente, mas no geral funcionava muito bem.
Quando um objeto precisava ser criado em vários locais, simplesmente tínhamos uma função (às vezes um método de classe desse objeto) para criar uma instância de produção. Essa função era chamada sempre que precisávamos desse objeto.
Nos testes, fizemos o mesmo: se várias vezes precisássemos criar uma 'versão de teste' de um objeto (ou seja, uma instância real suportada por objetos simulados), teríamos a função de criar essa 'versão de teste' de uma classe, para ser usado em testes.
Como eu disse, geralmente isso funcionou bem.
Agora estou entrando em um novo projeto Java e estamos decidindo se deve usar uma estrutura de DI ou DI manualmente (como no projeto anterior).
Por favor, explique como o trabalho com uma estrutura DI será diferente e de que maneira é melhor ou pior que a injeção manual de dependência 'vanilla'.
Edit: Eu também gostaria de acrescentar à pergunta: você testemunhou algum projeto de 'nível profissional' que faz DI manualmente, sem uma estrutura?
fonte
Respostas:
A chave dos contêineres DI é a abstração . Os contêineres DI abstraem essa preocupação de design para você. Como você pode imaginar, ele tem um impacto notável no código, muitas vezes traduzido em um número menor de construtores, montadores, fábricas e construtores. Em conseqüência, eles tornam seu código mais frouxamente acoplado (devido à IoC subjacente ), mais limpo e mais simples. Embora não sem um custo.
Planos de fundo
Anos atrás, essa abstração nos exigia escrever vários arquivos de configuração ( programação declarativa ). Foi fantástico ver o número de LOCs diminuindo substancialmente, mas enquanto os LOCSs diminuíram, o número de arquivos de configuração aumentou ligeiramente. Para projetos médios ou pequenos, não era um problema, mas para projetos maiores, a "montagem do código" dispersa em 50% entre os descritores de código e XML tornou-se uma dor de cabeça ... No entanto, o principal problema foi o paradigma em si. Foi bastante inflexível porque os descritores deixaram pouco espaço para personalizações.
O paradigma mudou progressivamente para a convenção sobre configuração , que troca os arquivos de configuração por anotações ou atributos. Ele mantém nosso código mais limpo e simples, fornecendo a flexibilidade que o XML não podia.
Provavelmente, essa é a diferença mais significativa em relação à forma como você estava trabalhando até agora. Menos código e mais mágica.
No lado negativo...
A convenção sobre configuração torna a abstração tão pesada que você mal sabe como ela funciona. Às vezes parece mágica e aqui vem mais uma desvantagem. Uma vez que está funcionando, muitos desenvolvedores não se preocupam com o funcionamento.
Esta é uma desvantagem para quem aprendeu DI com contêineres DI. Eles não entendem completamente a relevância dos padrões de design, as boas práticas e os princípios abstraídos pelo contêiner. Perguntei aos desenvolvedores se eles estavam familiarizados com o DI e suas vantagens e eles responderam: - Sim, eu usei o Spring IoC -. (O que diabos isso significa?!?)
Pode-se concordar ou não com cada uma dessas "desvantagens". Na minha humilde opinião, é necessário saber o que está acontecendo no seu projeto. Caso contrário, não temos um entendimento completo disso. Também é saber para que serve o DI e ter uma noção de como implementá-lo, é uma vantagem a considerar.
Do lado positivo...
Produtividade de uma palavra . As estruturas nos concentramos no que realmente importa. Os negócios do aplicativo.
Apesar das deficiências comentadas, para muitos de nós (cujo trabalho é condicionado por prazos e custos), essas ferramentas são um recurso inestimável. Na minha opinião, esse deve ser nosso principal argumento em favor da implementação de um contêiner de DI. Produtividade .
Teste
Se implementarmos DI puro ou contêineres, o teste não será um problema. Muito pelo contrário. Os mesmos contêineres geralmente nos fornecem as simulações para testes de unidade prontos para uso. Ao longo dos anos, usei meus testes como termômetros. Eles me dizem se eu confiei muito nas instalações de contêineres. Digo isso porque esses contêineres podem inicializar atributos privados sem setters ou construtores. Eles podem injetar componentes quase em qualquer lugar!
Parece tentador, certo? Seja cuidadoso! Não caia na armadilha !!
Sugestões
Se você decidir implementar contêineres, sugiro enfaticamente seguir as boas práticas. Continue implementando construtores, setters e interfaces. Aplique a estrutura / contêiner para usá-los . Facilitará possíveis migrações para outra estrutura ou remover a atual. Pode reduzir significativamente a dependência do contêiner.
Em relação a essas práticas:
Quando usar contêineres DI
Minha resposta será influenciada pela própria experiência, que é principalmente a favor dos contêineres DI (como eu comentei, estou muito focada na produtividade, então nunca tive a necessidade de DI puro. Muito pelo contrário).
Acho que você encontrará um artigo interessante de Mark Seeman sobre esse assunto, que também responde à pergunta sobre como implementar o DI puro .
Finalmente, se estivéssemos falando de Java, consideraria primeiro dar uma olhada no JSR330 - Injeção de Dependência .
Resumindo
Benefícios :
Desvantagens :
Diferenças com DI puro :
fonte
Na minha experiência, uma estrutura de injeção de dependência leva a vários problemas. Alguns dos problemas dependerão da estrutura e das ferramentas. Pode haver práticas que atenuam os problemas. Mas esses são os problemas que atingi.
Objetos não globais
Em alguns casos, você deseja injetar um objeto global: um objeto que terá apenas uma instância no aplicativo em execução. Isso pode ser um
MarkdownToHtmlRendererer
, umDatabaseConnectionPool
, o endereço para o seu servidor de API, etc. Para estes casos, os quadros de injeção de dependência trabalhar de maneira muito simples, basta adicionar a dependência necessários para o seu construtor e você tem isso.Mas e os objetos que não são globais? E se você precisar do usuário para a solicitação atual? E se você precisar da transação de banco de dados ativa no momento? E se você precisar do elemento HTML correspondente à div que contém um formulário em que é uma caixa de texto que acabou de disparar um evento?
A solução que eu vi empregada pelas estruturas de DI são os injetores hierárquicos. Mas isso torna o modelo de injeção mais complicado. Torna-se mais difícil descobrir exatamente o que será injetado em qual camada da hierarquia. Torna-se difícil saber se um determinado objeto existirá por aplicativo, por usuário, por solicitação, etc. Você não pode mais apenas adicionar suas dependências e fazê-lo funcionar.
Bibliotecas
Muitas vezes, você deseja ter uma biblioteca de códigos reutilizáveis. Isso também inclui as instruções de como construir objetos a partir desse código. Se você usar uma estrutura de injeção de dependência, isso normalmente incluirá regras de ligação. Se você quiser usar a biblioteca, adicione as regras de ligação delas às suas.
No entanto, vários problemas podem resultar. Você pode acabar com a mesma classe vinculada a diferentes implementações. A biblioteca pode esperar que certas ligações existam. A biblioteca pode substituir algumas ligações padrão, fazendo com que seu código faça algo estranho. Seu código pode substituir alguma ligação padrão, fazendo com que o código da biblioteca faça coisas estranhas.
Complexidade oculta
Ao escrever fábricas manualmente, você tem uma noção de como as dependências do aplicativo se encaixam. Quando você usa uma estrutura de injeção de dependência, não vê isso. Em vez disso, os objetos tendem a aumentar cada vez mais dependências até que mais cedo ou mais tarde tudo dependa de praticamente tudo.
Suporte IDE
Seu IDE provavelmente não entenderá a estrutura de injeção de dependência. Com fábricas manuais, você pode encontrar todos os chamadores de um construtor para descobrir todos os locais em que o objeto pode ser construído. Mas não encontrará as chamadas geradas pela estrutura DI.
Conclusão
O que obtemos de uma estrutura de injeção de dependência? Evitamos alguns clichês simples necessários para instanciar objetos. Sou a favor de eliminar clichês simplórios. No entanto, é fácil manter uma clichê simplificada. Se vamos substituir esse padrão, devemos insistir em substituí-lo por algo mais fácil. Devido às várias questões que discuti acima, não acho que as estruturas (pelo menos como estão) se encaixem no projeto.
fonte
A injeção de dependência Java + = estrutura é necessária?
Não.
O que uma estrutura DI me compra?
Construção de objeto acontecendo em uma linguagem que não é Java.
Se os métodos de fábrica são bons o suficiente para suas necessidades, você pode executar DI em java completamente sem estrutura, em 100% autêntico pojo java. Se suas necessidades vão além do que você tem outras opções.
Os padrões de design da Gangue dos Quatro realizam muitas coisas excelentes, mas sempre tiveram fraquezas no que diz respeito aos padrões criacionais. O próprio Java tem algumas fraquezas aqui. As estruturas de DI realmente ajudam mais com essa fraqueza criacional do que com as injeções reais. Você já sabe como fazer isso sem eles. O quanto de bom eles farão depende de quanta ajuda você precisa na construção do objeto.
Existem muitos padrões de criação que surgiram após a Gangue dos Quatro. O construtor Josh Bloch é um truque maravilhoso sobre a falta de parâmetros nomeados por java. Além disso, há construtores iDSL que oferecem muita flexibilidade. Mas cada um deles representa um trabalho significativo e padronizado.
As estruturas podem salvá-lo de parte desse tédio. Eles não são isentos de desvantagens. Se você não for cuidadoso, poderá se sentir dependente da estrutura. Tente alternar estruturas depois de usar uma e veja por si mesmo. Mesmo que você mantenha o xml ou as anotações genéricos de alguma maneira, poderá acabar apoiando o que são essencialmente duas linguagens que você deve mencionar lado a lado ao anunciar trabalhos de programação.
Antes de se apaixonar demais pelo poder das estruturas de DI, dedique algum tempo e investigue outras estruturas que oferecem recursos para os quais você poderia pensar que precisa de uma estrutura de DI. Muitos se ramificaram além do simples DI . Considere: Lombok , AspectJ e slf4J . Cada um se sobrepõe a algumas estruturas de DI, mas cada um funciona bem por conta própria.
Se o teste for realmente a força motriz por trás disso, verifique: jUnit (qualquer xUnit realmente) e Mockito (qualquer mock realmente) antes de começar a pensar que uma estrutura de DI deve dominar sua vida.
Você pode fazer o que quiser, mas, para um trabalho sério, prefiro uma caixa de ferramentas bem preenchida a um canivete suíço. Gostaria que fosse mais fácil traçar essas linhas, mas alguns se esforçaram muito para desfocá-las. Nada de errado com isso, mas pode tornar essas escolhas desafiadoras.
fonte
Criar classes e atribuir-lhes dependências faz parte do processo de modelagem, as partes divertidas. É a razão pela qual a maioria dos programadores gosta de programar.
Decidir qual implementação será usada e como as classes serão construídas é algo que precisará ser implementado de alguma forma e faz parte da configuração do aplicativo.
Escrever fábricas manualmente é um trabalho tedioso. As estruturas de DI facilitam a resolução automática de dependências que podem ser resolvidas e exigem configuração para as que não podem.
O uso de IDEs modernos permite navegar pelos métodos e seus usos. Ter fábricas escritas manualmente é muito bom, porque você pode simplesmente destacar o construtor de uma classe, mostrar seus usos e mostrar todos os locais onde e como a classe específica está sendo construída.
Mas mesmo com a estrutura DI, você pode ter um lugar central, como a configuração de construção de gráfico de objetos (OGCC a partir de agora), e se uma classe depende de dependências ambíguas, você pode simplesmente olhar para o OGCC e ver como uma classe específica está sendo construída. ali.
Você pode objetar que, pessoalmente, acha que poder ver os usos de um construtor específico é uma grande vantagem. Eu diria que o que sai melhor para você não é apenas subjetivo, mas vem principalmente da experiência pessoal.
Se você sempre trabalhou em projetos que se baseavam em estruturas de DI, realmente saberia apenas isso e, quando quisesse ver uma configuração de uma classe, sempre procuraria o OGCC e nem tentaria ver como os construtores são usados. , porque você saberia que todas as dependências são conectadas automaticamente de qualquer maneira.
Naturalmente, as estruturas DI não podem ser usadas para tudo e de vez em quando você terá que escrever um ou dois métodos de fábrica. Um caso muito comum é construir uma dependência durante a iteração sobre uma coleção, em que cada iteração fornece um sinalizador diferente que deve produzir uma implementação diferente de uma interface comum.
No final, provavelmente tudo se resumirá às suas preferências e ao que você está disposto a sacrificar (seja escrevendo fábricas ou transparência).
Experiência pessoal (aviso: subjetivo)
Falando como desenvolvedor PHP, embora eu goste da idéia por trás das estruturas de DI, eu as usei apenas uma vez. Foi quando a equipe com quem eu trabalhava deveria implantar um aplicativo muito rapidamente e não perdemos tempo escrevendo fábricas. Então, simplesmente pegamos uma estrutura de DI, configuramos e funcionou, de alguma forma .
Para a maioria dos projetos, gosto de ter as fábricas escritas manualmente, porque não gosto da magia negra que acontece nas estruturas de DI. Eu era o responsável por modelar uma classe e suas dependências. Fui eu quem decidiu por que o design tem a aparência que tem. Então, eu serei quem construirá adequadamente a classe (mesmo que a classe use dependências difíceis, como classes concretas em vez de interfaces).
fonte
Pessoalmente, não uso contêineres DI e não gosto deles. Eu tenho mais algumas razões para isso.
Geralmente é uma dependência estática. Portanto, é apenas um localizador de serviço com todas as suas desvantagens. Então, devido à natureza das dependências estáticas, elas ficam ocultas. Você não precisa lidar com eles, eles já estão de alguma forma lá e é perigoso, porque você deixa de controlá-los.
Ele quebra os princípios de POO , como coesão e a metáfora de objetos de tijolos Lego de David West .
Portanto, construir o objeto todo o mais próximo possível do ponto de entrada do programa é o caminho a seguir.
fonte
Como você já notou: Dependendo do idioma que você usa, existem diferentes pontos de vista, diferentes abordagens sobre como lidar com problemas semelhantes.
Em relação à injeção de dependência, ele se resume a isto: injeção de dependência é, como o termo diz, nada mais do que colocar as dependências um necessidades objeto em que objeto - contrastando o contrário: deixar o objeto em si instatiate instâncias de objetos que depende. Você dissocia a criação e o consumo de objetos para obter mais flexibilidade.
Se o seu design estiver bem estruturado, você geralmente tem poucas classes com muitas dependências; portanto, a instalação de dependências - manualmente ou por uma estrutura - deve ser relativamente fácil e a quantidade de clichê para escrever os testes deve ser fácil de gerenciar.
Dito isto, não há necessidade de uma estrutura DI.
Mas por que você deseja usar uma estrutura, no entanto?
I) Evite o código padrão
Se o seu projeto obtiver um tamanho, em que você sentirá a dor de escrever fábricas (e instatações) repetidamente, use uma estrutura DI
II) Abordagem declacativa da programação
Quando Java estava programando com XML , agora é: polvilhar poeira de fadas com anotações e a mágica acontece .
Mas falando sério: vejo uma clara vantagem disso. Você comunica claramente a intenção de dizer o que é necessário, em vez de onde encontrá-lo e como construí-lo.
tl; dr
As estruturas de DI não tornam sua base de código magicamente melhor, mas ajuda a tornar o código com boa aparência realmente nítido . Use-o quando achar necessário. Em um determinado momento do seu projeto, você economizará tempo e evitará náuseas.
fonte
Sim. Você provavelmente deve ler o livro de Mark Seemann intitulado "Injeção de Dependência". Ele descreve algumas boas práticas. Com base no que você disse, você pode se beneficiar. Você deve ter uma raiz de composição ou uma raiz de composição por unidade de trabalho (por exemplo, solicitação da web). Eu gosto do jeito dele de pensar que qualquer objeto que você cria é considerado uma dependência (assim como qualquer parâmetro para um método / construtor); portanto, você deve realmente ter cuidado com o local e como criar objetos. Uma estrutura ajudará você a manter esses conceitos claros. Se você ler o livro de Mark, encontrará mais do que apenas a resposta para sua pergunta.
fonte
Sim, ao trabalhar em Java, eu recomendaria uma estrutura DI. Existem algumas razões para isso:
O Java possui vários pacotes DI extremamente bons que oferecem injeção automatizada de dependência e também geralmente vêm com recursos adicionais mais difíceis de escrever manualmente do que o DI simples (por exemplo, dependências de escopo que permitem a conexão automática entre escopos, gerando código para procurar em que escopo deve estar use d encontrando a versão correta da dependência para esse escopo - assim, por exemplo, os objetos com escopo do aplicativo podem se referir a solicitar aqueles com escopo).
anotações java são extremamente úteis e fornecem uma excelente maneira de configurar dependências
a construção de objetos java é excessivamente detalhada em comparação com o que você está acostumado em python; o uso de uma estrutura aqui economiza mais trabalho do que em uma linguagem dinâmica.
As estruturas java DI podem fazer coisas úteis, como gerar automaticamente proxies e decoradores, que funcionam mais em Java do que em uma linguagem dinâmica.
fonte
Se seu projeto for pequeno o suficiente e você não tiver muita experiência com o DI-Framework, eu recomendaria não usar o framework e o faça manualmente.
Razão: Uma vez trabalhei em um projeto que usava duas bibliotecas que tinham dependências diretas para diferentes estruturas. ambos tinham código específico da estrutura como di-give-me-an-instance-of-XYZ. Tanto quanto eu sei, você só pode ter uma implementação de estrutura ativa por vez.
com um código como esse, você pode fazer mais mal do que benefício.
Se você tiver mais experiência, provavelmente abstrairá o trabalho de estrutura por uma interface (fábrica ou localizador de serviço), para que esse problema não exista mais e a estrutura de estrutura de trabalho traga mais benefícios.
fonte