Basicamente, a injeção de dependência faz algumas suposições (geralmente, mas nem sempre válidas) sobre a natureza de seus objetos. Se isso estiver errado, a DI pode não ser a melhor solução:
-
Primeiro, basicamente, a DI assume que o acoplamento rígido de implementações de objetos é SEMPRE ruim . Esta é a essência do Princípio da Inversão da Dependência: "uma dependência nunca deve ser feita após uma concreção; somente sobre uma abstração".
Isso fecha o objeto dependente para alterar com base em uma alteração na implementação concreta; uma classe dependendo do ConsoleWriter especificamente precisará mudar se a saída precisar ir para um arquivo, mas se a classe for dependente apenas de um IWriter expondo um método Write (), podemos substituir o ConsoleWriter atualmente sendo usado por um FileWriter e classe dependente não saberia a diferença (Princípio de Substituição de Liskhov).
No entanto, um design NUNCA pode ser fechado para todos os tipos de alteração; se o design da interface do IWriter for alterado, para adicionar um parâmetro ao Write (), um objeto de código extra (a interface do IWriter) deverá ser alterado, na parte superior do objeto / método de implementação e seu (s) uso (s). Se as mudanças na interface atual forem mais prováveis do que as mudanças na implementação da referida interface, o acoplamento frouxo (e o DI-ing de dependências fracamente acopladas) pode causar mais problemas do que resolve.
-
Segundo, e o corolário, DI assume que a classe dependente NUNCA é um bom lugar para criar uma dependência . Isso vai para o Princípio da Responsabilidade Única; se você tiver um código que crie uma dependência e também a use, haverá dois motivos pelos quais a classe dependente pode ter que mudar (uma alteração no uso OU na implementação), violando o SRP.
No entanto, novamente, adicionar camadas de indireção para DI pode ser uma solução para um problema que não existe; Se é lógico encapsular a lógica em uma dependência, mas essa lógica é a única implementação de uma dependência, então é mais doloroso codificar a resolução da dependência (injeção, local de serviço, fábrica) do que seria apenas use
new
e esqueça. -
Por fim, a DI, por sua natureza, centraliza o conhecimento de todas as dependências e suas implementações . Isso aumenta o número de referências que a montagem que executa a injeção deve ter e, na maioria dos casos, NÃO reduz o número de referências requeridas pelos conjuntos de classes dependentes reais.
ALGO, EM ALGUM LUGAR, deve ter conhecimento do dependente, da interface de dependência e da implementação de dependência para "conectar os pontos" e satisfazer essa dependência. DI tende a colocar todo esse conhecimento em um nível muito alto, seja em um contêiner IoC ou no código que cria objetos "principais", como o formulário principal ou o Controlador, que deve hidratar (ou fornecer métodos de fábrica para) as dependências. Isso pode colocar um monte de código necessariamente bem acoplado e muitas referências de montagem em altos níveis de seu aplicativo, que só precisa desse conhecimento para "escondê-lo" das classes dependentes reais (que de uma perspectiva muito básica é a melhor lugar para ter esse conhecimento, onde é usado).
Normalmente também não remove as referências mencionadas de baixo para baixo no código; um dependente ainda deve referenciar a biblioteca que contém a interface para sua dependência, que está em um dos três locais:
- tudo em uma única montagem de "Interfaces" que se torna muito centrada em aplicativos,
- cada um junto com a (s) implementação (ões) principal (is), removendo a vantagem de não ter que recompilar os dependentes quando as dependências mudam, ou
- um ou dois cada em montagens altamente coesas, o que aumenta a contagem de montagens, aumenta drasticamente os tempos de "construção total" e diminui o desempenho do aplicativo.
Tudo isso, mais uma vez, para resolver um problema em lugares onde não haja nenhum.