wake-up-neo.net

Qual è la differenza tra gli schemi di iniezione delle dipendenze e localizzatore di servizio?

Entrambi i modelli sembrano un'implementazione del principio di inversione del controllo. Cioè, che un oggetto non dovrebbe sapere come costruire le sue dipendenze. 

Iniezione di dipendenza (DI) sembra utilizzare un costruttore o un setter per "iniettare" le sue dipendenze. 

Esempio di utilizzo di Iniezione costruttore:  

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Service Locator sembra utilizzare un "contenitore", che collega le sue dipendenze e dà la sua barra. 

Esempio di utilizzo di un localizzatore di servizi:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

Poiché le nostre dipendenze sono solo oggetti stessi, queste dipendenze hanno dipendenze, che hanno ancora più dipendenze e così via e così via. Così è nato il contenitore Inversion of Control (o DI Container). Esempi: Castle Windsor, Ninject, Structure Map, Spring, ecc.)

Ma un contenitore IOC/DI sembra esattamente come un localizzatore di servizi. Lo chiama un contenitore DI un brutto nome? Un container IOC/DI è solo un altro tipo di Service Locator? È la sfumatura nel fatto che usiamo i contenitori DI per lo più quando abbiamo molte dipendenze?

250
Charles Graham

La differenza può sembrare lieve, ma anche con ServiceLocator, la classe è ancora responsabile della creazione delle sue dipendenze. Usa solo il localizzatore di servizi per farlo. Con DI, alla classe vengono date le sue dipendenze. Non sa, né importa da dove vengono. Un risultato importante di questo è che l'esempio DI è molto più facile da testare l'unità - perché è possibile passarlo a implementazioni fittizie dei suoi oggetti dipendenti. Potresti combinare i due - e iniettare il localizzatore di servizio (o una fabbrica), se lo volessi.

156
tvanfosson

Quando si utilizza un localizzatore di servizi, ogni classe avrà una dipendenza dal proprio localizzatore di servizi. Questo non è il caso dell'iniezione di dipendenza. Generalmente, l'iniettore delle dipendenze verrà chiamato una sola volta all'avvio per iniettare dipendenze in alcune classi principali. Le classi di questa classe principale dipendono in modo ricorsivo in modo ricorsivo delle loro dipendenze, fino a quando non si ottiene un grafico completo degli oggetti.

Un buon confronto: http://martinfowler.com/articles/injection.html

Se il tuo iniettore di dipendenze sembra un localizzatore di servizi, dove le classi chiamano direttamente l'iniettore, probabilmente non è un iniettore di dipendenze, ma piuttosto un localizzatore di servizi.

78
Joel

I locatori di servizi nascondono le dipendenze: non è possibile distinguere guardando un oggetto se colpisce un database o meno (ad esempio) quando ottiene le connessioni da un localizzatore. Con l'iniezione di dipendenza (almeno l'iniezione del costruttore) le dipendenze sono esplicite.

Inoltre, i localizzatori di servizi interrompono l'incapsulamento perché forniscono un punto di accesso globale alle dipendenze di altri oggetti. Con il localizzatore di servizi, come con qualsiasi singleton :

diventa difficile specificare il pre e il post condizioni per l'oggetto client interfaccia, perché il funzionamento del suo l'implementazione può essere ingerita con da fuori.

Con l'iniezione di dipendenza, una volta specificate le dipendenze di un oggetto, esse sono sotto il controllo dell'oggetto stesso.

41
Jeff Sternal

Martin Fowler afferma

Con service locator la classe dell'applicazione lo richiede esplicitamente da un messaggio al localizzatore. Con l'iniezione non c'è una richiesta esplicita, il servizio appare nella classe dell'applicazione - da qui l'inversione di controllo.

In breve: Service Locator e Dependency Injection sono solo implementazioni di Dependency Inversion Principle.

Il principio importante è "dipende dalle astrazioni, non dalle concrezioni". Questo renderà il tuo software progettato "liberamente accoppiato", "estensibile", "flessibile".

Puoi usare quello che si adatta meglio alle tue esigenze. Per una grande applicazione, avendo una base di codice enorme, è meglio usare un Localizzatore di servizio, perché la Dipendenza dell'iniezione richiederebbe più modifiche al codice base.

Puoi controllare questo post: Dependency Inversion: Service Locator o Dependency Injection

Anche il classico: Inversion of Control Containers e il pattern In Dependency Injection di Martin Fowler

Progettazione di classi riutilizzabili di Ralph E. Johnson e Brian Foote 

Tuttavia, quello che mi ha aperto gli occhi è stato: ASP.NET MVC: Resolve o Inject? Questo è il problema ... di Dino Esposito

31
Nathan

Una classe che usa il costruttore DI indica di consumare codice che ci sono dipendenze da soddisfare. Se la classe utilizza la SL internamente per recuperare tali dipendenze, il codice che consuma non è a conoscenza delle dipendenze. Questo può sembrare superficiale, ma in realtà è utile sapere di eventuali dipendenze esplicite. È meglio da una vista architettonica. E quando si esegue il test, è necessario sapere se una classe ha bisogno di determinate dipendenze e configurare la SL per fornire versioni false appropriate di tali dipendenze. Con DI, basta passare i falsi. Non un'enorme differenza, ma è lì.

DI e SL possono lavorare insieme, però. È utile avere una posizione centrale per le dipendenze comuni (ad esempio impostazioni, logger, ecc.). Data una classe che utilizza tali servizi, è possibile creare un costruttore "reale" che riceve i deps e un costruttore predefinito (nessun parametro) che recupera dalla SL e inoltra al costruttore "reale".

EDIT: e, ovviamente, quando si usa la SL, si sta introducendo un certo accoppiamento con quel componente. Il che è ironico, poiché l'idea di tale funzionalità è incoraggiare le astrazioni e ridurre l'accoppiamento. Le preoccupazioni possono essere bilanciate e dipende da quanti posti avresti bisogno di usare la SL. Se fatto come suggerito sopra, solo nel costruttore di classi predefinito.

19
Grant Palin

Nel mio ultimo progetto li uso entrambi. Uso l'injection dependency per la testabilità dell'unità. Uso il localizzatore di servizi per nascondere l'implementazione e dipendere dal mio contenitore IoC. e sì! Una volta che usi uno dei contenitori IoC (Unity, Ninject, Windsor Castle), ne fai affidamento. E una volta che è obsoleto o per qualche motivo se si vorrà scambiarlo, si dovrà/potrebbe aver bisogno di cambiare la propria implementazione - almeno la composizione root. Ma il localizzatore di servizi astrae quella fase.

Come non dovresti dipendere dal tuo contenitore IoC? O dovrai avvolgerlo da te (che è una cattiva idea) o utilizzare Service Locator per configurare il tuo contenitore IoC. Quindi dirai al localizzatore di servizi quale interfaccia hai bisogno e chiamerà il contenitore di IoC configurato per recuperare quell'interfaccia.

Nel mio caso, utilizzo ServiceLocator che è un componente framework. E usa Unity per il contenitore IoC. Se in futuro avrò bisogno di scambiare il mio contenitore IoC in Ninject tutto quello che devo fare è che devo configurare il mio localizzatore di servizio per usare Ninject invece di Unity. Facile migrazione.

Ecco un grande articolo spiega questo scenario; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/

6
Teoman shipahi

Penso che i due lavori insieme.

Iniezione di dipendenza significa che inserisci una classe/interfaccia dipendente in una classe che consuma (di solito nel suo costruttore). Questo disaccoppia le due classi tramite un'interfaccia e significa che la classe consumatrice può lavorare con molti tipi di implementazioni di "dipendenza iniettata". 

Il ruolo del localizzatore di servizi è di mettere insieme la tua implementazione. Si imposta un localizzatore di servizi tramite alcune cinghie di avvio all'inizio del programma. Il bootstrap è il processo di associazione di un tipo di implementazione a un particolare astratto/interfaccia. Che viene creato per te in fase di esecuzione. (basato su configurazione o bootstrap). Se non avessi implementato l'integrazione delle dipendenze, sarebbe molto difficile utilizzare un localizzatore di servizi o un contenitore IOC.

5
NoelAdy

Una ragione per aggiungere, ispirata da un aggiornamento della documentazione che abbiamo scritto per il progetto MEF la scorsa settimana (aiuto a costruire MEF).

Una volta che un'app è composta da potenzialmente migliaia di componenti, può essere difficile determinare se un particolare componente può essere istanziato correttamente. Con "istanziato correttamente", intendo che in questo esempio basato sul componente Foo, un'istanza di IBar e sarà disponibile, e che il componente che lo fornisce:

  • hanno le sue dipendenze richieste,
  • non essere coinvolto in alcun ciclo di dipendenza non valido, e
  • nel caso di MEF, essere fornito con una sola istanza.

Nel secondo esempio che hai dato, dove il costruttore va al contenitore IoC per recuperare le sue dipendenze, l'unico modo per testare che un'istanza di Foo sarà istanziata correttamente con la configurazione di runtime effettiva della tua app è per effettivamente lo costruisce.

Questo ha tutti i tipi di effetti collaterali imbarazzanti al momento del test, perché il codice che funzionerà in fase di runtime non funzionerà necessariamente sotto un'imbracatura di test. I mazzi non lo faranno, perché la vera configurazione è la cosa che dobbiamo testare, non una configurazione di test-time.

La radice di questo problema è la differenza già richiamata da @Jon: l'injection delle dipendenze attraverso il costruttore è dichiarativa, mentre la seconda versione utilizza il pattern imperativo di localizzazione del servizio.

Un contenitore IoC, se usato con attenzione, può analizzare in modo statico la configurazione runtime della tua app senza creare effettivamente alcuna istanza dei componenti coinvolti. Molti contenitori popolari forniscono alcune variazioni di questo; Microsoft.Composition, che è la versione di MEF per il targeting di .NET 4.5 e applicazioni in stile Metro, fornisce un campione CompositionAssert nella documentazione wiki. Usandolo, puoi scrivere codice come:

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

CompositionAssert.CanExportSingle<Foo>(container);

(Vedi questo esempio ).

Verificando i Composition Roots della tua applicazione al momento del test puoi potenzialmente rilevare alcuni errori che potrebbero altrimenti passare attraverso i test più avanti nel processo.

Spero che questa sia un'aggiunta interessante a questo insieme di risposte altrimenti esauriente sull'argomento!

5

Entrambi sono tecniche di implementazione di IoC. Esistono anche altri pattern che implementano Inversion of Control:

  • modello di fabbrica
  • localizzatore di servizi
  • iniezione dipendenza (iniezione costruttore, iniezione parametri (se non richiesta), iniezione setter di iniezione interfaccia) ...

Service locator e DI sembrano più simili, entrambi utilizzano il contenitore per definire le dipendenze, che mappa l'astrazione per l'implementazione concreta.

La differenza principale è il modo in cui si trovano le dipendenze, nel codice del client Posizione servizio richiesta le dipendenze, in DI si usa il contenitore per creare tutti gli oggetti e si inietta la dipendenza come parametri del costruttore (o proprietà). 

5
Nininea

In questo caso troppo semplificato non c'è differenza e possono essere usati in modo intercambiabile. Tuttavia, i problemi del mondo reale non sono così semplici. Supponiamo che la stessa classe Bar abbia un'altra dipendenza chiamata D. In tal caso il tuo servizio di localizzazione non sarebbe in grado di risolvere quella dipendenza e tu dovresti istanziarla all'interno della classe D; perché è responsabilità delle tue classi istanziare le loro dipendenze. Andrebbe anche peggio se la classe D avesse altre dipendenze e nelle situazioni del mondo reale di solito diventa ancora più complicata di così. In tali scenari, DI è una soluzione migliore di ServiceLocator.

3
Daniel

Nota: non sto esattamente rispondendo alla domanda. Tuttavia, ritengo che ciò possa essere utile per i nuovi studenti del modello di Iniezione di Dipendenza che sono confusi su di esso con il Localizzatore di servizio (anti-) schema che capita di imbattersi in questa pagina.

Conosco la differenza tra il Service Locator (sembra essere considerato come un anti-pattern ora) e gli schemi di Iniezione delle dipendenze e posso comprendere esempi concreti su ciascun modello, ma sono stato confuso da esempi che mostrano un localizzatore di servizio all'interno del costruttore (supponiamo che noi facendo l'iniezione del costruttore).

"Service Locator" è spesso usato sia come nome di un pattern, sia come nome per riferirsi all'oggetto (supponiamo anche) usato in quel pattern per ottenere oggetti senza usare il nuovo operatore. Ora, lo stesso tipo di oggetto può essere usato anche in composition root per eseguire l'iniezione di dipendenza, ed è qui che entra in gioco la confusione.

Il punto è che è possibile che si stia utilizzando un oggetto del servizio di localizzazione all'interno di un costruttore DI, ma non si sta utilizzando il modello "Service Locator". È meno confuso se lo si fa riferimento come oggetto contenitore IoC, come si può intuire che essenzialmente fanno la stessa cosa (correggimi se sbaglio).

Che si tratti di un localizzatore di servizi (o solo di un localizzatore) o di un contenitore IoC (o solo di un contenitore) non fa alcuna differenza, come si può intuire, poiché probabilmente si riferiscono alla stessa astrazione (correggimi se ho torto ). E 'solo che chiamandolo "localizzatore di servizi" si suggerisce che si sta utilizzando l'anti-pattern di Service Locator insieme al pattern di Iniezione di dipendenza.

IMHO, nominandolo "localizzatore" invece di "posizione" o "localizzazione", può anche far pensare a volte che il localizzatore di servizio in un articolo si riferisca al contenitore di Service Locator e non al pattern di localizzazione del servizio (anti-) , specialmente quando c'è un modello correlato chiamato Iniezione di dipendenza e non Iniettore di dipendenza.

3
blizpasta

Qual è la differenza (se esiste) tra l'iniezione delle dipendenze e il localizzatore di servizio? Entrambi i modelli sono in grado di implementare il principio di inversione delle dipendenze. Il pattern Locator del servizio è più facile da usare in una base di codice esistente poiché rende il design generale più flessibile senza imporre modifiche all'interfaccia pubblica. Per lo stesso motivo, il codice basato sul modello di Service Locator è meno leggibile rispetto al codice equivalente basato su Dipendenza dell'iniezione.

Il pattern Dipendenza Iniezione rende chiaro fin dalla firma quali dipendenze avrà una classe (o un metodo). Per questo motivo, il codice risultante è più pulito e più leggibile.

1
Yogesh Joshi