Ambos os padrões parecem uma implementação do princípio de inversão de controle. Ou seja, que um objeto não deve saber como construir suas dependências.
A Injeção de Dependência (DI) parece usar um construtor ou setter para "injetar" suas dependências.
Exemplo de uso de injeção de construtor:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
//...
}
O Localizador de Serviço parece usar um "contêiner", que conecta suas dependências e fornece a ele sua barra.
Exemplo de uso de um localizador de serviço:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Como nossas dependências são apenas objetos, essas dependências têm dependências, que têm ainda mais dependências, e assim por diante. Assim, nasceu o Inversion of Control Container (ou DI Container). Exemplos: Castelo Windsor, Ninject, Mapa da Estrutura, Primavera, etc.)
Mas um contêiner IOC / DI se parece exatamente com um localizador de serviço. Está chamando de Container DI um nome ruim? Um contêiner IOC / DI é apenas outro tipo de localizador de serviço? A nuance é o fato de usarmos DI Containers principalmente quando temos muitas dependências?
fonte
Respostas:
A diferença pode parecer pequena, mas mesmo com o ServiceLocator, a classe ainda é responsável por criar suas dependências. Ele apenas usa o localizador de serviço para fazer isso. Com o DI, a classe recebe suas dependências. Não sabe nem se importa de onde eles vêm. Um resultado importante disso é que o exemplo de DI é muito mais fácil para o teste de unidade - porque você pode passar por implementações simuladas de seus objetos dependentes. Você pode combinar os dois - e injetar o localizador de serviço (ou uma fábrica), se desejar.
fonte
Quando você usa um localizador de serviço, todas as classes dependem do localizador de serviço. Este não é o caso da injeção de dependência. O injetor de dependência normalmente será chamado apenas uma vez na inicialização para injetar dependências em alguma classe principal. As classes das quais essa classe principal depende recursivamente terão suas dependências injetadas, até que você tenha um gráfico de objeto completo.
Uma boa comparação: http://martinfowler.com/articles/injection.html
Se o seu injetor de dependência se parece com um localizador de serviço, onde as classes chamam o injetor diretamente, provavelmente não é um injetor de dependência, mas um localizador de serviço.
fonte
Os localizadores de serviço ocultam dependências - você não pode dizer, olhando para um objeto, se ele atinge um banco de dados ou não (por exemplo) quando obtém conexões de um localizador. Com injeção de dependência (pelo menos injeção de construtor), as dependências são explícitas.
Além disso, os localizadores de serviço quebram o encapsulamento porque fornecem um ponto global de acesso às dependências de outros objetos. Com o localizador de serviço, como em qualquer singleton :
Com a injeção de dependência, depois que as dependências de um objeto são especificadas, elas estão sob controle do próprio objeto.
fonte
With dependency injection (at least constructor injection) the dependencies are explicit.
. Por favor explique.Martin Fowler afirma :
Resumindo: Localizador de Serviço e Injeção de Dependência são apenas implementações do Princípio de Inversão de Dependência.
O princípio importante é "Dependa de abstrações, não de concretizações". Isso tornará seu design de software "pouco acoplado", "extensível", "flexível".
Você pode usar o que melhor se adapta às suas necessidades. Para um grande aplicativo, com uma enorme base de código, é melhor usar um Localizador de Serviço, porque a Injeção de Dependência exigiria mais alterações em sua base de código.
Você pode verificar esta publicação: Inversão de Dependências: Localizador de Serviço ou Injeção de Dependência
Também o clássico: Inversão de contêineres de controle e o padrão de injeção de dependência de Martin Fowler
Design de classes reutilizáveis por Ralph E. Johnson & Brian Foote
No entanto, o que abriu meus olhos foi: ASP.NET MVC: resolver ou injetar? Essa é a questão… de Dino Esposito
fonte
Uma classe que usa o construtor DI indica para o código de consumo que existem dependências a serem satisfeitas. Se a classe usar o SL internamente para recuperar essas dependências, o código de consumo não estará ciente das dependências. Na superfície, isso pode parecer melhor, mas é realmente útil conhecer quaisquer dependências explícitas. É melhor do ponto de vista arquitetônico. E ao fazer o teste, você precisa saber se uma classe precisa de determinadas dependências e configurar o SL para fornecer versões falsas apropriadas dessas dependências. Com o DI, apenas passe as falsificações. Não é uma diferença enorme, mas está lá.
DI e SL podem trabalhar juntos, no entanto. É útil ter um local central para dependências comuns (por exemplo, configurações, registrador, etc). Dada uma classe usando esses deps, você pode criar um construtor "real" que recebe os deps e um construtor padrão (sem parâmetro) que recupera do SL e encaminha para o construtor "real".
EDIT: e, é claro, quando você usa o SL, está introduzindo algum acoplamento a esse componente. O que é irônico, uma vez que a idéia de tal funcionalidade é incentivar abstrações e reduzir o acoplamento. As preocupações podem ser equilibradas e depende de quantos lugares você precisaria para usar o SL. Se feito como sugerido acima, apenas no construtor de classe padrão.
fonte
Ambos são técnicas de implementação de IoC. Existem também outros padrões que implementam Inversão de controle:
O localizador de serviço e o Container DI parecem mais semelhantes, ambos usam um container para definir dependências, que mapeia a abstração para a implementação concreta.
A principal diferença é como as dependências estão localizadas, no Service Locator, o código do cliente solicita as dependências, no DI Container usamos um contêiner para criar todos os objetos e injeta dependência como parâmetros (ou propriedades) do construtor.
fonte
No meu último projeto eu uso os dois. Uso injeção de dependência para testar a unidade. Eu uso o localizador de serviço para ocultar a implementação e estar dependente do meu contêiner de IoC. e sim! Depois de usar um dos contêineres IoC (Unity, Ninject, Castelo de Windsor), você depende dele. E uma vez desatualizado ou por algum motivo, se você desejar trocá-lo, precisará / poderá alterar sua implementação - pelo menos a raiz da composição. Mas o localizador de serviço abstrai essa fase.
Como você não dependeria do seu contêiner de IoC? Você precisará agrupá-lo por conta própria (o que é uma má idéia) ou usar o Service Locator para configurar seu contêiner de IoC. Então, você instruirá o localizador de serviço a obter qual interface você precisa e chamará o contêiner IoC configurado para recuperar essa interface.
No meu caso, eu uso o ServiceLocator, que é um componente da estrutura. E use o Unity para contêiner de IoC. Se, no futuro, eu precisar trocar meu contêiner de IoC para o Ninject, tudo o que preciso fazer é configurar o localizador de serviço para usar o Ninject em vez do Unity. Migração fácil.
Aqui está um ótimo artigo explica esse cenário; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/
fonte
Eu acho que os dois trabalham juntos.
Injeção de dependência significa que você envia uma classe / interface dependente para uma classe consumidora (geralmente para o construtor). Isso desacopla as duas classes por meio de uma interface e significa que a classe consumidora pode trabalhar com muitos tipos de implementações de "dependência injetada".
O papel do localizador de serviço é reunir sua implementação. Você configura um localizador de serviço através de algumas correias de inicialização no início do seu programa. Bootstrapping é o processo de associar um tipo de implementação a um resumo / interface específico. Que é criado para você em tempo de execução. (com base na sua configuração ou inicialização). Se você não tivesse implementado a injeção de dependência, seria muito difícil utilizar um localizador de serviço ou um contêiner de IOC.
fonte
Um motivo a acrescentar, inspirado em uma atualização de documentação que escrevemos para o projeto MEF na semana passada (eu ajudo a criar o MEF).
Depois que um aplicativo é composto de potencialmente milhares de componentes, pode ser difícil determinar se algum componente específico pode ser instanciado corretamente. Por "instanciado corretamente", quero dizer que, neste exemplo, com base no
Foo
componente, uma instância deIBar
e estará disponível e que o componente que o fornece:No segundo exemplo que você deu, em que o construtor vai ao contêiner de IoC para recuperar suas dependências, a única maneira de testar se uma instância de
Foo
poderá instanciar corretamente com a configuração real do tempo de execução do seu aplicativo é realmente construir isso .Isso tem todos os tipos de efeitos colaterais desagradáveis no momento do teste, porque o código que funcionará no tempo de execução não necessariamente funcionará sob um equipamento de teste. A zombaria não funciona, porque a configuração real é a coisa que precisamos testar, não uma configuração em tempo de teste.
A raiz desse problema é a diferença já mencionada pelo @Jon: a injeção de dependências por meio do construtor é declarativa, enquanto a segunda versão usa o imperativo padrão do Localizador de Serviços.
Um contêiner de IoC, quando usado com cuidado, pode analisar estaticamente a configuração de tempo de execução do seu aplicativo sem realmente criar nenhuma instância dos componentes envolvidos. Muitos contêineres populares fornecem algumas variações disso; O Microsoft.Composition , que é a versão do MEF direcionada para aplicativos da Web .NET e estilo Metro 4.5, fornece um
CompositionAssert
exemplo na documentação do wiki. Usando-o, você pode escrever código como:(Veja este exemplo ).
Ao verificar as Raízes da composição do seu aplicativo no momento do teste, é possível detectar alguns erros que, de outra forma, podem passar por testes posteriormente no processo.
Espero que esta seja uma adição interessante a este conjunto abrangente de respostas sobre o assunto!
fonte
Nota: não estou respondendo exatamente à pergunta. Mas acho que isso pode ser útil para os novos alunos do padrão de Injeção de Dependência, que estão confusos com o padrão do Localizador de Serviço (anti-), que por acaso tropeça nesta página.
Conheço a diferença entre o Service Locator (parece ser um antipadrão agora) e os padrões de Injeção de Dependência e posso entender exemplos concretos de cada padrão, mas fiquei confuso com exemplos mostrando um localizador de serviço dentro do construtor (suponha que ' está fazendo injeção de construtor).
O "Service Locator" é frequentemente usado como o nome de um padrão e como o nome para se referir ao objeto (assuma também) usado nesse padrão para obter objetos sem usar o novo operador. Agora, esse mesmo tipo de objeto também pode ser usado na raiz da composição para executar a injeção de dependência, e é aí que entra a confusão.
O ponto importante é que você pode estar usando um objeto localizador de serviço dentro de um construtor de DI, mas não está usando o "padrão Localizador de Serviço". É menos confuso se o referirmos como um objeto de contêiner de IoC, pois você deve ter adivinhado que eles essencialmente fazem a mesma coisa (me corrija se eu estiver errado).
Se é referido como um localizador de serviço (ou apenas localizador), ou como um contêiner de IoC (ou apenas contêiner), não faz diferença, como você deve imaginar, pois provavelmente estão se referindo à mesma abstração (me corrija se eu estiver errado) ) É apenas que chamá-lo de localizador de serviço sugere que alguém está usando o antipadrão do Localizador de Serviço junto com o padrão de Injeção de Dependência.
O IMHO, nomeando-o um 'localizador' em vez de 'local' ou 'localizando', também pode levar às pessoas a pensar que o localizador de serviço em um artigo está se referindo ao contêiner do Localizador de Serviço e não ao padrão (anti-) do Localizador de Serviço , especialmente quando há um padrão relacionado chamado Injeção de dependência e não Injetor de dependência.
fonte
Nesse caso simplificado demais, não há diferença e eles podem ser usados de forma intercambiável. No entanto, problemas do mundo real não são tão simples. Apenas suponha que a própria classe Bar possuísse outra dependência denominada D. Nesse caso, o localizador de serviço não seria capaz de resolvê-la e você teria que instancia-la na classe D; porque é da responsabilidade de suas classes instanciar suas dependências. Ficaria ainda pior se a própria classe D tivesse outras dependências e, em situações do mundo real, ela geralmente se torna ainda mais complicada do que isso. Em tais cenários, o DI é uma solução melhor que o ServiceLocator.
fonte
bar
própria classe tiver uma dependência,bar
também haverá localizador de serviço, esse é o objetivo de usar DI / IoC.Qual é a diferença (se houver) entre injeção de dependência e localizador de serviço? Ambos os padrões são bons em implementar o princípio da Inversão de Dependências. O padrão do Localizador de Serviço é mais fácil de usar em uma base de código existente, pois torna o design geral mais flexível, sem forçar alterações na interface pública. Por esse mesmo motivo, o código baseado no padrão Service Locator é menos legível que o código equivalente baseado na injeção de dependência.
O padrão de Injeção de Dependência deixa claro desde a assinatura quais dependências uma classe (ou método) terá. Por esse motivo, o código resultante é mais limpo e mais legível.
fonte
Seguir uma concepção simples me deu uma compreensão mais clara da diferença entre o Service Locator e o DI Container:
O Localizador de Serviço é usado no consumidor e extrai serviços por ID de algum armazenamento, mediante solicitação direta do consumidor
O DI Container está localizado em algum lugar externo e recebe serviços de algum armazenamento e os envia ao consumidor (não importa via construtor ou método)
No entanto, podemos falar sobre a diferença entre eles apenas no contexto do uso concreto do consumidor. Quando o Service Locator e o DI Container são usados na raiz da composição, eles são quase semelhantes.
fonte
O contêiner DI é um localizador de superconjunto de serviços. Pode ser usado para localizar um serviço , com capacidade adicional de montar (conectar) as injeções de dependência .
fonte
Para o registro
A menos que você realmente precise de uma interface (a interface é usada por mais de uma classe), NÃO DEVE USÁ-LO . Nesse caso, o IBar permite utilizar qualquer classe de serviço que a implemente. No entanto, geralmente, essa interface será usada por uma única classe.
Por que é uma má idéia usar uma interface? Porque é realmente difícil depurar.
Por exemplo, digamos que a instância "bar" falhou, pergunta: qual classe falhou ?. Qual código devo corrigir? Uma visão simples, leva a uma interface e é aqui que minha estrada termina.
Em vez disso, se o código usar uma dependência grave, é fácil depurar um erro.
Se "bar" falhar, devo verificar e disparar a classe BarService.
fonte
contract
e apenas define um comportamento e não a ação. Em vez de repassar o objeto real, apenas a interface é compartilhada para que o consumidor não acesse o restante do seu objeto. Também para testes de unidade, ajuda a testar apenas a parte que precisa ser testada. Acho que com o tempo você entenderia sua utilidade.