Todos nós sabemos como os solteiros são ruins porque ocultam dependências e por outros motivos .
Mas em um framework, pode haver muitos objetos que precisam ser instanciados apenas uma vez e chamados de qualquer lugar (logger, db etc.).
Para resolver este problema, fui instruído a usar o chamado "Gerenciador de Objetos" (ou Contêiner de Serviço como o symfony) que armazena internamente todas as referências a Serviços (logger etc).
Mas por que um Provedor de Serviços não é tão ruim quanto um Singleton puro?
O provedor de serviços também oculta as dependências e apenas conclui a criação da primeira instância. Portanto, estou realmente lutando para entender por que devemos usar um provedor de serviços em vez de singletons.
PS. Eu sei que para não esconder dependências devo usar DI (conforme afirmado por Misko)
Adicionar
Eu acrescentaria: hoje em dia, os singletons não são tão maus, o criador do PHPUnit explicou aqui:
DI + Singleton resolve o problema:
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
isso é muito inteligente, mesmo que não resolva todos os problemas.
Além de DI e Service Container, há alguma solução aceitável para acessar esses objetos auxiliares?
fonte
Respostas:
O Service Locator é apenas o menor de dois males, por assim dizer. O "menor" se resumindo a essas quatro diferenças ( pelo menos não consigo pensar em nenhuma outra agora ):
Princípio de Responsabilidade Única
O Service Container não viola o Princípio de Responsabilidade Única, como o Singleton faz. Singletons combinam criação de objetos e lógica de negócios, enquanto o Service Container é estritamente responsável por gerenciar os ciclos de vida de objetos de seu aplicativo. Nesse sentido, o Service Container é melhor.
Acoplamento
Os singletons geralmente são codificados permanentemente em seu aplicativo devido às chamadas de método estático, o que leva a dependências fortemente acopladas e difíceis de simular em seu código. O SL, por outro lado, é apenas uma classe e pode ser injetado. Portanto, embora todas as suas classes dependam disso, pelo menos é uma dependência fracamente acoplada. Portanto, a menos que você implemente o ServiceLocator como um Singleton, isso é um pouco melhor e também mais fácil de testar.
No entanto, todas as classes que usam o ServiceLocator agora dependerão do ServiceLocator, que também é uma forma de acoplamento. Isso pode ser atenuado usando uma interface para o ServiceLocator, de forma que você não esteja vinculado a uma implementação ServiceLocator concreta, mas suas classes dependerão da existência de algum tipo de Locator, ao passo que não usar um ServiceLocator aumenta drasticamente a reutilização.
Dependências ocultas
Porém, o problema de ocultar dependências existe muito. Quando você apenas injeta o localizador em suas classes de consumo, você não conhecerá nenhuma dependência. Mas, em contraste com o Singleton, o SL geralmente instancia todas as dependências necessárias nos bastidores. Então, quando você busca um serviço, você não termina como Misko Hevery no exemplo do CreditCard , por exemplo , você não tem que instanciar todas as dependências das dependências manualmente.
Buscar as dependências de dentro da instância também está violando a Lei de Demeter , que afirma que você não deve pesquisar colaboradores. Uma instância deve falar apenas com seus colaboradores imediatos. Este é um problema com Singleton e ServiceLocator.
Estado Global
O problema do estado global também é um pouco mitigado porque quando você instancia um novo Service Locator entre os testes, todas as instâncias criadas anteriormente são excluídas também (a menos que você cometeu o erro e as salvou em atributos estáticos no SL). Isso não vale para nenhum estado global em classes administradas pelo SL, é claro.
Consulte também Fowler em Service Locator vs Dependency Injection para uma discussão muito mais detalhada.
Uma observação sobre sua atualização e o artigo vinculado por Sebastian Bergmann sobre o código de teste que usa Singletons : Sebastian não sugere, de forma alguma, que a solução alternativa proposta torna o uso de Singleons menos problemático. É apenas uma maneira de tornar o código que de outra forma seria impossível de testar mais testável. Mas ainda é um código problemático. Na verdade, ele observa explicitamente: "Só porque você pode, não significa que você deveria".
fonte
O padrão do localizador de serviço é um antipadrão. Isso não resolve o problema de expor dependências (você não pode dizer olhando para a definição de uma classe quais são suas dependências porque elas não estão sendo injetadas, em vez disso, estão sendo arrancadas do localizador de serviço).
Portanto, sua pergunta é: por que os localizadores de serviço são bons? Minha resposta é: eles não são.
Evite, evite, evite.
fonte
O container de serviço oculta dependências como o padrão Singleton faz. Você pode sugerir o uso de contêineres de injeção de dependência, já que ele tem todas as vantagens do contêiner de serviço, mas nenhuma desvantagem (pelo que eu sei) desse contêiner de serviço.
Pelo que entendi, a única diferença entre os dois é que no container de serviço, o container de serviço é o objeto que está sendo injetado (ocultando assim as dependências), quando você usa DIC, o DIC injeta as dependências apropriadas para você. A classe sendo gerenciada pelo DIC é completamente alheia ao fato de que é gerenciada por um DIC, portanto, você tem menos acoplamento, dependências claras e testes de unidade felizes.
Esta é uma boa pergunta no SO, explicando a diferença de ambos: Qual é a diferença entre os padrões de injeção de dependência e localizador de serviço?
fonte
Porque você pode facilmente substituir objetos no Service Container por
1) herança (a classe Object Manager pode ser herdada e os métodos podem ser substituídos)
2) alteração da configuração (no caso do Symfony)
E, Singletons são ruins não apenas por causa do alto acoplamento, mas porque são _ Single _tons. É uma arquitetura errada para quase todos os tipos de objetos.
Com DI 'puro' (em construtores) você pagará um preço muito alto - todos os objetos devem ser criados antes de serem passados no construtor. Isso significará mais memória usada e menos desempenho. Além disso, nem sempre o objeto pode ser apenas criado e passado no construtor - uma cadeia de dependências pode ser criada ... Meu inglês não é bom o suficiente para discutir sobre isso completamente, leia sobre isso na documentação do Symfony.
fonte
Para mim, tento evitar constantes globais, singletons por um motivo simples, há casos em que posso precisar de APIs em execução.
Por exemplo, tenho front-end e admin. No admin, quero que eles possam fazer o login como um usuário. Considere o código dentro do admin.
Isso pode estabelecer uma nova conexão de banco de dados, novo logger etc. para a inicialização do frontend e verificar se o usuário realmente existe, é válido, etc. Também usaria cookies separados e serviços de localização adequados.
Minha ideia de singleton é - Você não pode adicionar o mesmo objeto dentro do pai duas vezes. Por exemplo
deixaria você com uma única instância e ambas as variáveis apontando para ela.
Finalmente, se você deseja usar o desenvolvimento orientado a objetos, trabalhe com objetos, não com classes.
fonte
$api
var pelo seu framework? Eu não entendi exatamente o que você quis dizer. Além disso, se a chamadaadd('Logger')
retornar a mesma instância, basicamente, você tem um co