Perguntas mais freqüentes sobre DI (FAQ)
DI é outro nome para IoC?
A Inversion of Control (IoC) é um princípio focado na forma como o código é executado – se seu código inicia
o código externo ou se seu código é integrado ao código externo, que então o chama. IoC é um conceito amplo que inclui eventos, o chamado Princípio de Hollywood, e outros aspectos. Fábricas,
que fazem parte da Regra #3: Deixe a Fábrica tratar disso, e
representam a inversão para o operador new
, também são componentes deste conceito.
A Dependency Injection (DI) é sobre como um objeto sabe sobre outro objeto, ou seja, dependência. É um padrão de desenho que requer a passagem explícita de dependências entre objetos.
Assim, pode-se dizer que a DI pode ser uma forma específica de IoC. No entanto, nem todas as formas de IoC são adequadas em termos de pureza de código. Por exemplo, entre os anti-padrões, incluímos todas as técnicas que funcionam com o estado global ou o chamado Service Locator.
O que é um Localizador de Serviços?
Um Localizador de Serviços é uma alternativa à Injeção de Dependência. Ele funciona criando um armazenamento central onde todos os serviços ou dependências disponíveis são registrados. Quando um objeto precisa de uma dependência, ele o solicita ao Service Locator.
Entretanto, em comparação com a Injeção de Dependência, ela perde transparência: as dependências não são passadas diretamente aos objetos e, portanto, não são facilmente identificáveis, o que exige o exame do código para descobrir e compreender todas as conexões. Os testes também são mais complicados, pois não podemos simplesmente passar objetos simulados para os objetos testados, mas temos que passar pelo Localizador de Serviços. Além disso, o Localizador de Serviços perturba o projeto do código, pois objetos individuais devem estar cientes de sua existência, o que difere da Injeção de Dependência, onde os objetos não têm conhecimento do recipiente DI.
Quando é melhor não usar DI?
Não há dificuldades conhecidas associadas ao uso do padrão de projeto de injeção de dependência. Pelo contrário, a obtenção de dependências a partir de locais globalmente acessíveis leva a uma série de complicações, assim como o uso de um Localizador de Serviços. Portanto, é aconselhável usar sempre o DI. Esta não é uma abordagem dogmática, mas simplesmente não foi encontrada uma alternativa melhor.
Entretanto, há certas situações em que não passamos objetos um para o outro e os obtemos do espaço global. Por exemplo, ao depurar um código e precisar descarregar um valor variável em um ponto específico do programa, medir a duração de uma determinada parte do programa, ou registrar uma mensagem. Nesses casos, quando se trata de ações temporárias que serão posteriormente removidas do código, é legítimo usar um dumper, cronômetro ou registrador globalmente acessível. Estas ferramentas, afinal, não pertencem ao projeto do código.
O uso de DI tem seus inconvenientes?
O uso da Injeção de Dependência envolve alguma desvantagem, como o aumento da complexidade de escrita de código ou pior desempenho? O que perdemos quando começamos a escrever código de acordo com a DI?
DI não tem impacto no desempenho da aplicação ou nos requisitos de memória. O desempenho do Recipiente DI pode desempenhar um papel, mas no caso da Nette DI, o recipiente é compilado em PHP puro, de modo que suas despesas gerais durante o tempo de execução da aplicação são essencialmente zero.
Ao escrever o código, é necessário criar construtoras que aceitem dependências. No passado, isto poderia ser demorado, mas graças às modernas IDEs e à promoção da propriedade dos construtores, agora é uma questão de poucos segundos. As fábricas podem ser facilmente geradas usando Nette DI e um plugin PhpStorm com apenas alguns cliques. Por outro lado, não há necessidade de escrever singletons e pontos de acesso estáticos.
Pode-se concluir que uma aplicação devidamente projetada usando DI não é nem mais curta nem mais longa em comparação com uma aplicação usando singletons. Partes do código que funcionam com dependências são simplesmente extraídas de classes individuais e movidas para novos locais, ou seja, o container DI e as fábricas.
Como reescrever um aplicativo legado para DI?
A migração de uma aplicação herdada para a Injeção de Dependência pode ser um processo desafiador, especialmente para aplicações grandes e complexas. É importante abordar este processo de forma sistemática.
- Ao passar para a Injeção de Dependência, é importante que todos os membros da equipe compreendam os princípios e práticas que estão sendo utilizados.
- Primeiro, realizar uma análise da aplicação existente para identificar os componentes-chave e suas dependências. Crie um plano para quais peças serão refatoradas e em que ordem.
- Implementar um recipiente DI ou, melhor ainda, utilizar uma biblioteca existente, como a Nette DI.
- Refatorar gradualmente cada parte da aplicação para usar a Injeção de Dependência. Isto pode envolver a modificação de construtores ou métodos para aceitar dependências como parâmetros.
- Modificar os lugares no código onde os objetos de dependência são criados de modo que as dependências sejam injetadas pelo contêiner. Isto pode incluir o uso de fábricas.
Lembre-se de que passar para a Injeção de Dependência é um investimento na qualidade do código e na sustentabilidade a longo prazo da aplicação. Embora possa ser um desafio fazer estas mudanças, o resultado deve ser um código mais limpo, mais modular e facilmente testável que esteja pronto para futuras extensões e manutenção.
Por que a composição é preferível à herança?
É preferível usar a composição em vez da herança porque ela serve para reutilizar o código sem ter de se preocupar com as consequências das alterações. Assim, ela proporciona um acoplamento mais frouxo, em que não precisamos nos preocupar com o fato de que a alteração de algum código causará a necessidade de alterar outro código dependente. Um exemplo típico é uma situação conhecida como inferno do construtor.
O Nette DI Container pode ser usado fora da Nette?
Absolutamente. O Nette DI Container faz parte da Nette, mas foi projetado como uma biblioteca independente que pode ser usada independentemente de outras partes da estrutura. Basta instalá-lo usando o Composer, criar um arquivo de configuração definindo seus serviços, e depois usar algumas linhas de código PHP para criar o container DI. E você pode começar imediatamente a aproveitar a Injeção de Dependência em seus projetos.
O capítulo Nette DI Container descreve como é um caso de uso específico, incluindo o código.
Por que a configuração está nos arquivos NEON?
NEON é uma linguagem de configuração simples e de fácil leitura desenvolvida dentro da Nette para a criação de aplicações, serviços e suas dependências. Em comparação com JSON ou YAML, oferece opções muito mais intuitivas e flexíveis para este fim. Em NEON, você pode descrever naturalmente as ligações que não seriam possíveis de escrever em Symfony & YAML, ou apenas através de uma descrição complexa.
A análise dos arquivos NEON torna a aplicação mais lenta?
Embora os arquivos NEON sejam analisados muito rapidamente, este aspecto não importa muito. A razão é que os arquivos de análise ocorrem apenas uma vez durante o primeiro lançamento do aplicativo. Depois disso, o código do recipiente DI é gerado, armazenado no disco e executado para cada pedido subseqüente sem a necessidade de análise posterior.
É assim que funciona em um ambiente de produção. Durante o desenvolvimento, os arquivos NEON são analisados cada vez que seu conteúdo muda, garantindo que o desenvolvedor tenha sempre um container DI atualizado. Como mencionado anteriormente, a análise propriamente dita é uma questão de um instante.
Como posso acessar os parâmetros do arquivo de configuração em minha classe?
Tenha em mente a Regra nº 1: Deixe que seja passada a você. Se uma classe requer informações de um arquivo de configuração, não precisamos descobrir como acessar essas informações; em vez disso, simplesmente pedimos por elas – por exemplo, através do construtor da classe. E realizamos a passagem no arquivo de configuração.
Neste exemplo, %myParameter%
é um espaço reservado para o valor do parâmetro myParameter
, que
será passado para o construtor de MyClass
:
# config.neon
parameters:
myParameter: Some value
services:
- MyClass(%myParameter%)
Se você quiser passar vários parâmetros ou usar a fiação automática, é útil embrulhar os parâmetros em um objeto.
A Nette suporta a interface do Container PSR-11?
A Nette DI Container não suporta diretamente o PSR-11. Entretanto, se você precisar de interoperabilidade entre o Container Nette DI e bibliotecas ou estruturas que esperam a interface do Container PSR-11, você pode criar um adaptador simples para servir como ponte entre o Container Nette DI e o PSR-11.