Qual é a diferença entre os padrões de Injeção de Dependência e Localizador de Serviço?

Ambos os padrões parecem uma implementação do princípio da inversão de controle. Ou seja, que um object não deve saber como construir suas dependencies.

A Injeção de Dependência (DI) parece usar um construtor ou setter para “injetar” suas dependencies.

Exemplo de uso da injeção de construtor:

//Foo Needs an IBar public class Foo { private IBar bar; public Foo(IBar bar) { this.bar = bar; } //... } 

O Service Locator parece usar um “contêiner”, que liga suas dependencies e fornece uma barra.

Exemplo de uso de um Service Locator:

 //Foo Needs an IBar public class Foo { private IBar bar; public Foo() { this.bar = Container.Get(); } //... } 

Como nossas dependencies são apenas objects em si, essas dependencies têm dependencies, que têm ainda mais dependencies e assim por diante. Assim, nasceu o Inversion of Control Container (ou contêiner DI). Exemplos: Castle Windsor, Ninject, Mapa de Estruturas, Spring, etc.)

Mas um contêiner IOC / DI parece exatamente com um localizador de serviços. Está chamando um contêiner DI um nome ruim? Um contêiner IOC / DI é apenas outro tipo de localizador de serviço? A nuance do fato de usarmos Contêineres DI principalmente quando temos muitas Dependências?

   

A diferença pode parecer pequena, mas mesmo com o ServiceLocator, a class ainda é responsável por criar suas dependencies. Apenas usa o localizador de serviços para fazer isso. Com DI, a class recebe suas dependencies. Não sabe nem se importa de onde eles vêm. Um resultado importante disso é que o exemplo de DI é muito mais fácil de testar na unidade – porque você pode passar implementações simuladas de seus objects dependentes. Você poderia combinar os dois – e injetar o localizador de serviço (ou uma fábrica), se você quisesse.

Quando você usa um localizador de serviços, cada turma terá uma dependência do localizador de serviços. Este não é o caso da injeção de dependência. O injetor de dependência normalmente será chamado apenas uma vez na boot para injetar dependencies em alguma class principal. As classs das quais esta class principal depende recursivamente terão suas dependencies injetadas, até que você tenha um gráfico de object completo.

Uma boa comparação: http://martinfowler.com/articles/injection.html

Se seu injetor de dependência se parece com um localizador de serviço, onde as classs chamam o injetor diretamente, ele provavelmente não é um injetor de dependência, mas sim um localizador de serviço.

Os localizadores de serviço escondem dependencies – você não pode dizer, olhando para um object, se ele atinge um database ou não (por exemplo) quando ele obtém conexões de um localizador. Com injeção de dependência (pelo menos, injeção de construtor), as dependencies são explícitas.

Além disso, os localizadores de serviço interrompem o encapsulamento porque fornecem um ponto global de access às dependencies de outros objects. Com o localizador de serviços, como acontece com qualquer singleton :

torna-se difícil especificar as condições pré e pós para a interface do object cliente, porque o funcionamento de sua implementação pode ser interferido externamente.

Com a injeção de dependência, uma vez que as dependencies de um object são especificadas, elas estão sob o controle do próprio object.

Martin Fowler afirma :

Com o localizador de serviço, a class do aplicativo solicita explicitamente por uma mensagem ao localizador. Com a injeção não há solicitação explícita, o serviço aparece na class da aplicação – daí a inversão do controle.

Em resumo: Service Locator e Dependency Injection são apenas implementações do Princípio de Inversão de Dependência.

O princípio importante é: “Dependam das Abstrações, não das Concretions”. Isso fará com que seu design de software seja “fracamente acoplado”, “extensível”, “flexível”.

Você pode usar o que melhor atenda às suas necessidades. Para uma grande aplicação, ter uma grande base de código, é melhor usar um Service Locator, porque a Injeção de Dependência exigiria mais alterações na sua base de código.

Você pode conferir este post: Inversão de Dependência: 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 por Martin Fowler

Criando classs reutilizáveis por Ralph E. Johnson & Brian Foote

No entanto, o que abriu meus olhos foi: ASP.NET MVC: Resolve ou Inject? Essa é a questão … por Dino Esposito

Uma class usando o construtor DI indica ao código consumidor que existem dependencies a serem satisfeitas. Se a class usa o SL internamente para recuperar tais dependencies, o código consumidor não está ciente das dependencies. Isso pode parecer melhor na superfície, mas na verdade é útil saber de quaisquer dependencies explícitas. É melhor do ponto de vista arquitetônico. E ao fazer o teste, você precisa saber se uma class precisa de certas dependencies e configurar o SL para fornecer versões falsas apropriadas dessas dependencies. Com DI, basta passar as falsificações. Não é uma diferença enorme, mas está lá.

DI e SL podem trabalhar juntos, no entanto. É útil ter uma localização central para dependencies comuns (por exemplo, configurações, logger, etc). Dada uma class que usa tais 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, você está introduzindo algum acoplamento para esse componente. O que é irônico, já que a ideia de tal funcionalidade é encorajar abstrações e reduzir o acoplamento. As preocupações podem ser equilibradas e depende de quantos lugares você precisaria usar o SL. Se feito como sugerido acima, apenas no construtor de class padrão.

Eu acho que os dois trabalham juntos.

dependency injection significa que você empurra alguma class / interface dependente para uma class consumidora (normalmente para seu construtor). Isso separa as duas classs por meio de uma interface e significa que a class consumidora pode trabalhar com muitos tipos de implementações de “dependência injetada”.

A function do localizador de serviços é reunir sua implementação. Você configura um localizador de serviços através de algumas correias de boot no início do seu programa. Bootstrapping é o processo de associar um tipo de implementação a um resumo / interface particular. Que é criado para você em tempo de execução. (baseado em você config ou bootstrap). Se você não tivesse implementado a injeção de dependência, seria muito difícil utilizar um localizador de serviços ou um contêiner IOC.

Ambos são técnicas de implementação do IoC. Existem também outros padrões que implementam a Inversão de Controle:

  • padrão de fábrica
  • localizador de serviço
  • injeção de dependência (injeção de construtor, injeção de parâmetro (se não for necessário), injeção de setter de injeção de interface) …

O localizador de serviço e a DI parecem mais semelhantes, ambos usam contêiner para definir dependencies, o que mapeia a abstração para a implementação concreta.

A principal diferença é como as dependencies estão localizadas, no código de cliente do Service Location solicitamos as dependencies, no DI utilizamos container para criar todos os objects e injeta dependência como parâmetros construtivos (ou propriedades).

No meu último projeto eu uso os dois. Eu uso injeção de dependência para testabilidade de unidade. Eu uso o localizador de serviço para ocultar a implementação e ser dependente do meu contêiner IoC. e sim! Depois de usar um dos contêineres IoC (Unity, Ninject, Windsor Castle), você depende dele. E uma vez que esteja desatualizado ou por algum motivo, se você quiser trocá-lo, você poderá / precisar 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 IoC? Ou você precisará include o seu próprio (o que é uma má ideia) ou usar o Service Locator para configurar seu contêiner IoC. Assim, você dirá ao service locator para obter a interface de que precisa e chamará o contêiner IoC configurado para recuperar essa interface.

No meu caso, eu uso o ServiceLocator, que é um componente de estrutura. E use o Unity para o contêiner IoC. Se, no futuro, precisar trocar meu contêiner IoC para Ninject, tudo o que preciso fazer é configurar meu localizador de serviços para usar Ninject em vez de Unity. Migração fácil.

Aqui está um ótimo artigo explica este cenário; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/

Um motivo para adicionar, inspirado em uma atualização de documentação que escrevemos para o projeto MEF na semana passada (eu ajudo a construir o MEF).

Uma vez que um aplicativo é composto potencialmente de 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 baseado no componente Foo , uma instância do IBar e estará disponível, e que o componente que o fornece irá:

  • tem suas dependencies necessárias,
  • não estar envolvido em nenhum ciclo de dependência inválido e
  • no caso do MEF, ser fornecido com apenas uma instância.

No segundo exemplo que você deu, onde o construtor vai para o contêiner IoC para recuperar suas dependencies, a única maneira de testar se uma instância do Foo poderá ser instanciada corretamente com a configuração real do tempo de execução do seu aplicativo é realmente construí-lo .

Isso tem todos os tipos de efeitos colaterais desagradáveis ​​no tempo de teste, porque o código que funcionará em tempo de execução não funcionará necessariamente sob um equipamento de teste. Mocks não serve, porque a configuração real é a coisa que precisamos testar, não uma configuração de tempo de teste.

A raiz deste problema é a diferença já chamada por @Jon: injetar dependencies através do construtor é declarativo, enquanto a segunda versão usa o padrão imperativo Service Locator.

Um contêiner IoC, quando usado com cuidado, pode analisar estaticamente a configuração de tempo de execução do seu aplicativo sem realmente criar quaisquer instâncias dos componentes envolvidos. Muitos contêineres populares fornecem alguma variação disso; A Microsoft.Composition , que é a versão do MEF que tem como alvo os aplicativos do tipo .NET 4.5 e estilo Metro, fornece uma amostra do CompositionAssert na documentação do wiki. Usando isso, você pode escrever código como:

  // Whatever you use at runtime to configure the container var container = CreateContainer(); CompositionAssert.CanExportSingle(container); 

(Veja este exemplo ).

Ao verificar as raízes de composição de seu aplicativo no momento do teste, você pode detectar alguns erros que podem, de outra forma, passar por testes no processo.

Espero que este seja um acréscimo interessante a este conjunto abrangente de respostas sobre o tópico!

Nota: não estou exatamente respondendo a pergunta. Mas eu sinto que isso pode ser útil para os novos aprendizes do padrão Injeção de Dependência que estão confusos sobre isso com o padrão (anti) do Localizador de Serviço que por acaso tropeçam nesta página.

Eu sei a diferença entre o Service Locator (ele parece ser considerado como um antipadrão agora) e 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 nós está fazendo injeção de construtor).

“Service Locator” é frequentemente usado como o nome de um padrão, e como o nome para se referir ao object (assuma também) usado naquele padrão para obter objects sem usar o novo operador. Agora, esse mesmo tipo de object 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.

A questão é que você pode estar usando um object de localização de serviço dentro de um construtor de DI, mas você não está usando o “padrão de Service Locator”. É menos confuso se alguém o referir como um object contêiner IoC, como você pode ter imaginado 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 IoC (ou apenas contêiner) não faz diferença, como você adivinha, já que provavelmente estão se referindo à mesma abstração (corrija-me se estiver errado ). É só que chamar isso de um localizador de serviço sugere que alguém está usando o anti- padrão Service Locator junto com o padrão de Injeção de Dependência.

IMHO, nomeando-o como ‘localizador’ em vez de ‘localização’ ou ‘localização’, também pode fazer com que algumas pessoas pensem que o localizador de serviço em um artigo está se referindo ao contêiner do Service Locator e não ao localizador de serviço (anti-) , especialmente quando há um padrão relacionado chamado Injeção de Dependência e não Injetor de Dependência.

Neste caso super simplificado, não há diferença e eles podem ser usados ​​de forma intercambiável. No entanto, os problemas do mundo real não são tão simples. Apenas suponha que a class Bar em si tivesse outra dependência chamada D. Nesse caso, seu localizador de serviço não seria capaz de resolver essa dependência e você teria que instanciá-la na class D; porque é de responsabilidade de suas classs instanciar suas dependencies. Ficaria ainda pior se a própria class D tivesse outras dependencies e, em situações do mundo real, ela geralmente fica ainda mais complicada do que isso. Em tais cenários, DI é uma solução melhor que o ServiceLocator.

Qual é a diferença (se houver) entre Injeção de Dependência e Localizador de Serviço? Ambos os padrões são bons para implementar o princípio de inversão de dependência. O padrão Service Locator é mais fácil de usar em uma base de código existente, pois torna o design geral mais flexível, sem forçar mudanças na interface pública. Por esse mesmo motivo, o código que é baseado no padrão Service Locator é menos legível do 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 dependencies uma class (ou um método) terá. Por esse motivo, o código resultante é mais limpo e mais legível.