Perguntas Frequentes sobre DI (FAQ)
- DI é outro nome para IoC?
- O que é Service Locator?
- Quando é melhor não usar DI?
- O uso de DI tem desvantagens?
- Como reescrever uma aplicação legada para DI?
- Por que a composição é preferida em relação à herança?
- É possível usar o Nette DI Container fora do Nette?
- Por que a configuração está em arquivos NEON?
- A análise de arquivos NEON não torna a aplicação mais lenta?
- Como acesso os parâmetros do arquivo de configuração a partir da minha classe?
- Nette suporta a interface PSR-11: Container?
DI é outro nome para IoC?
Inversion of Control (IoC) é um princípio focado na maneira como o código é executado – se o seu código
executa código de terceiros ou se o seu código é integrado a código de terceiros que o chama posteriormente. IoC é um termo
amplo que inclui eventos, o chamado Princípio de Hollywood e outros aspectos. Parte
deste conceito também são as fábricas, sobre as quais fala a Regra nº 3: deixe para a
fábrica, e que representam uma inversão para o operador new
.
Dependency Injection (DI) foca na maneira como um objeto aprende sobre outro objeto, ou seja, sobre suas dependências. É um padrão de projeto que exige a passagem explícita de dependências entre objetos.
Pode-se dizer, portanto, que DI é uma forma específica de IoC. No entanto, nem todas as formas de IoC são adequadas do ponto de vista da pureza do código. Por exemplo, entre os antipadrões estão técnicas que trabalham com estado global ou o chamado Service Locator.
O que é Service Locator?
É uma alternativa à Injeção de Dependência. Funciona criando um repositório central onde todos os serviços ou dependências disponíveis são registrados. Quando um objeto precisa de uma dependência, ele a solicita ao Service Locator.
No entanto, em comparação com a Injeção de Dependência, perde em transparência: as dependências não são passadas diretamente aos objetos e não são tão facilmente identificáveis, o que exige examinar o código para revelar e entender todas as ligações. O teste também é mais complicado, pois não podemos simplesmente passar objetos mock para os objetos testados, mas temos que passar pelo Service Locator. Além disso, o Service Locator perturba o design do código, pois objetos individuais precisam saber de sua existência, o que difere da Injeção de Dependência, onde os objetos não têm conhecimento do contêiner DI.
Quando é melhor não usar DI?
Não são conhecidas dificuldades associadas ao uso do padrão de projeto Injeção de Dependência. Pelo contrário, obter dependências de locais globalmente disponíveis leva a uma série de complicações, assim como o uso do Service Locator. Portanto, é aconselhável usar DI sempre. Isso não é uma abordagem dogmática, mas simplesmente não foi encontrada uma alternativa melhor.
No entanto, existem certas situações em que não passamos objetos e os obtemos do espaço global. Por exemplo, ao depurar código, quando você precisa imprimir o valor de uma 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 tarefas temporárias que serão posteriormente removidas do código, é legítimo usar um dumper, cronômetro ou logger globalmente disponível. Essas ferramentas não pertencem ao design do código.
O uso de DI tem desvantagens?
O uso da Injeção de Dependência traz alguma desvantagem, como aumento da complexidade na escrita do código ou piora no desempenho? O que perdemos quando começamos a escrever código de acordo com DI?
DI não tem impacto no desempenho ou nos requisitos de memória da aplicação. O desempenho do Contêiner DI pode desempenhar algum papel, mas no caso do Nette DI, o contêiner é compilado em PHP puro, então sua sobrecarga durante a execução da aplicação é essencialmente zero.
Ao escrever código, geralmente é necessário criar construtores que aceitam dependências. Antigamente, isso podia ser demorado, mas graças aos IDEs modernos e à promoção de propriedades do construtor, agora é uma questão de segundos. As fábricas podem ser facilmente geradas usando Nette DI e o plugin para PhpStorm com um clique do mouse. Por outro lado, elimina-se a necessidade de escrever singletons e pontos de acesso estáticos.
Pode-se afirmar que uma aplicação corretamente projetada usando DI não é nem mais curta nem mais longa em comparação com uma aplicação usando singletons. As partes do código que trabalham com dependências são apenas extraídas das classes individuais e movidas para novos locais, ou seja, para o contêiner DI e fábricas.
Como reescrever uma aplicação legada para DI?
A transição de uma aplicação legada para Injeção de Dependência pode ser um processo desafiador, especialmente para aplicações grandes e complexas. É importante abordar este processo sistematicamente.
- Ao fazer a transição para Injeção de Dependência, é importante que todos os membros da equipe entendam os princípios e procedimentos que estão sendo usados.
- Primeiro, realize uma análise da aplicação existente e identifique os componentes chave e suas dependências. Crie um plano de quais partes serão refatoradas e em que ordem.
- Implemente um contêiner DI ou, melhor ainda, use uma biblioteca existente, como Nette DI.
- Refatore gradualmente partes individuais da aplicação para usar Injeção de Dependência. Isso pode incluir a modificação de construtores ou métodos para aceitar dependências como parâmetros.
- Modifique os locais no código onde objetos com dependências são criados para que, em vez disso, as dependências sejam injetadas pelo contêiner. Isso pode incluir o uso de fábricas.
Lembre-se que a transição para Injeção de Dependência é um investimento na qualidade do código e na sustentabilidade a longo prazo da aplicação. Embora possa ser desafiador fazer essas mudanças, o resultado deve ser um código mais limpo, modular e facilmente testável, pronto para futuras extensões e manutenção.
Por que a composição é preferida em relação à herança?
É preferível usar composição em vez de herança, porque ela serve para reutilizar código sem ter que nos preocupar com as consequências das mudanças. Ela fornece, portanto, um acoplamento mais fraco, onde não precisamos nos preocupar que a mudança em algum código cause a necessidade de mudar outro código dependente. Um exemplo típico é a situação conhecida como inferno de construtores.
É possível usar o Nette DI Container fora do Nette?
Com certeza. O Nette DI Container faz parte do Nette, mas foi projetado como uma biblioteca independente que pode ser usada independentemente de outras partes do framework. Basta instalá-lo usando o Composer, criar um arquivo de configuração com a definição de seus serviços e, em seguida, usar algumas linhas de código PHP para criar o contêiner DI. E você pode começar imediatamente a aproveitar os benefícios da Injeção de Dependência em seus projetos.
O uso específico, incluindo códigos, é descrito no capítulo Nette DI Container.
Por que a configuração está em arquivos NEON?
NEON é uma linguagem de configuração simples e fácil de ler, desenvolvida no Nette para configurar 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 propósito. Em NEON, é possível descrever naturalmente ligações que em Symfony & YAML não seriam possíveis de escrever, ou apenas por meio de uma descrição complexa.
A análise de arquivos NEON não torna a aplicação mais lenta?
Embora os arquivos NEON sejam analisados muito rapidamente, este aspecto não importa. A razão é que a análise dos arquivos ocorre apenas uma vez na primeira execução da aplicação. Depois disso, o código do contêiner DI é gerado, salvo em disco e executado em cada requisição subsequente, sem a necessidade de realizar análises adicionais.
É assim que funciona em um ambiente de produção. Durante o desenvolvimento, os arquivos NEON são analisados toda vez que seu conteúdo é alterado, para que o desenvolvedor sempre tenha um contêiner DI atualizado. A análise em si é, como mencionado, uma questão de momento.
Como acesso os parâmetros do arquivo de configuração a partir da minha classe?
Lembre-se da Regra nº 1: peça para receber. Se uma classe requer informações do arquivo de configuração, não precisamos pensar em como obter essas informações, em vez disso, simplesmente as solicitamos – por exemplo, através do construtor da classe. E realizamos a passagem no arquivo de configuração.
Neste exemplo, %myParameter%
é um placeholder para o valor do parâmetro myParameter
, que é
passado para o construtor da classe MyClass
:
# config.neon
parameters:
myParameter: Some value
services:
- MyClass(%myParameter%)
Se você deseja passar vários parâmetros ou usar autowiring, é aconselhável envolver os parâmetros em um objeto.
Nette suporta a interface PSR-11: Container?
O Nette DI Container não suporta PSR-11 diretamente. No entanto, se você precisar de interoperabilidade entre o Nette DI Container e bibliotecas ou frameworks que esperam a Interface de Contêiner PSR-11, você pode criar um adaptador simples que servirá como uma ponte entre o Nette DI Container e o PSR-11.