Senza dependency injectionEdit

Nel seguente esempio Java, la classe Client contiene una variabile membro del servizio inizializzata dal costruttore Client. Il cliente controlla quale implementazione del servizio viene utilizzata e controlla la sua costruzione. In questa situazione, si dice che il client abbia una dipendenza hard-coded su ExampleService.

// An example without dependency injectionpublic class Client { // Internal reference to the service used by this client private ExampleService service; // Constructor Client() { // Specify a specific implementation in the constructor instead of using dependency injection service = new ExampleService(); } // Method within this client that uses the services public String greet() { return "Hello " + service.getName(); }}

L’iniezione di dipendenze è una tecnica alternativa per inizializzare la variabile membro piuttosto che creare esplicitamente un oggetto di servizio come mostrato sopra., Possiamo regolare questo esempio utilizzando le varie tecniche descritte e illustrate nelle sottosezioni di seguito.

Tipi di dependency injectionEdit

Ci sono almeno tre modi in cui un oggetto client può ricevere un riferimento a un modulo esterno:

constructor injection Le dipendenze sono fornite tramite il costruttore di classe di un client. setter injection Il client espone un metodo setter che l’iniettore utilizza per iniettare la dipendenza. iniezione dell’interfaccia L’interfaccia della dipendenza fornisce un metodo di iniettore che inietterà la dipendenza in qualsiasi client passato ad esso., I client devono implementare un’interfaccia che esponga un metodo setter che accetta la dipendenza.

Other typesEdit

È possibile che i framework DI abbiano altri tipi di iniezione oltre a quelli presentati sopra.

I framework di test possono anche utilizzare altri tipi. Alcuni framework di test moderni non richiedono nemmeno che i client accettino attivamente l’iniezione di dipendenze, rendendo così testabile il codice legacy. In particolare, nel linguaggio Java, è possibile utilizzare reflection per rendere pubblici gli attributi privati durante il test e quindi accettare iniezioni per assegnazione.,

Alcuni tentativi di inversione del controllo non forniscono la rimozione completa della dipendenza, ma sostituiscono semplicemente una forma di dipendenza con un’altra. Come regola generale, se un programmatore non può guardare altro che il codice client e dire quale framework viene utilizzato, allora il client ha una dipendenza hard-coded sul framework.

Costruttore injectionEdit

Questo metodo richiede al client di fornire un parametro in un costruttore per la dipendenza.

Setter injectionEdit

Questo metodo richiede al client di fornire un metodo setter per la dipendenza.,

Interface injectionEdit

Questo è semplicemente il client che pubblica un’interfaccia di ruolo per i metodi setter delle dipendenze del client. Può essere usato per stabilire come l’iniettore dovrebbe parlare con il client quando si iniettano le dipendenze.

// Service setter interface.public interface ServiceSetter { public void setService(Service service);}// Client classpublic class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use. @Override public void setService(Service service) { this.service = service; }}

Constructor injection comparisonEdit

Preferito quando tutte le dipendenze possono essere costruite per prime perché può essere usato per garantire che l’oggetto client sia sempre in uno stato valido, invece di avere alcuni dei suoi riferimenti alle dipendenze essere null (non essere impostato)., Tuttavia, da solo, manca la flessibilità per far cambiare le sue dipendenze in seguito. Questo può essere un primo passo per rendere il client immutabile e quindi thread sicuro.

Setter injection comparisonEdit

Richiede al client di fornire un metodo setter per ogni dipendenza. Questo dà la libertà di manipolare lo stato dei riferimenti di dipendenza in qualsiasi momento. Questo offre flessibilità, ma se c’è più di una dipendenza da iniettare, è difficile per il client assicurarsi che tutte le dipendenze vengano iniettate prima che il client possa essere fornito per l’uso.,

Poiché queste iniezioni avvengono indipendentemente, non c’è modo di dire quando l’iniettore è finito di cablare il client. Una dipendenza può essere lasciata null semplicemente dall’iniettore che non riesce a chiamare il suo setter. Questo costringe il controllo che l’iniezione è stata completata da quando il cliente è assemblato a ogni volta che viene utilizzato.

Interface injection comparisonEdit

Il vantaggio di interface injection è che le dipendenze possono essere completamente ignoranti dei loro client ma possono ancora ricevere un riferimento a un nuovo client e, usandolo, inviare un riferimento a sé al client., In questo modo, le dipendenze diventano iniettori. La chiave è che il metodo di iniezione (che potrebbe essere solo un classico metodo setter) viene fornito attraverso un’interfaccia.

È ancora necessario un assemblatore per introdurre il client e le sue dipendenze. L’assemblatore prende un riferimento al client, lo trasmette all’interfaccia setter che imposta quella dipendenza e lo passa a quell’oggetto di dipendenza che si gira e passa un riferimento a sé al client.,

Affinché l’iniezione dell’interfaccia abbia valore, la dipendenza deve fare qualcosa oltre a restituire semplicemente un riferimento a se stessa. Questo potrebbe fungere da factory o sub-assembler per risolvere altre dipendenze, astraendo così alcuni dettagli dall’assemblatore principale. Potrebbe essere il conteggio dei riferimenti in modo che la dipendenza sappia quanti client lo stanno usando. Se la dipendenza mantiene una raccolta di client, potrebbe in seguito iniettarli tutti con un’istanza diversa di se stessa.,

Esempi di assemblaggiomodifica

L’assemblaggio manuale in main a mano è un modo per implementare l’iniezione delle dipendenze.

L’esempio precedente costruisce manualmente il grafico dell’oggetto e quindi lo richiama a un certo punto per avviarlo a funzionare. Importante notare è che questo iniettore non è puro. Usa uno degli oggetti che costruisce. Ha una relazione puramente costruttiva con ExampleService ma mescola la costruzione e l’utilizzo del Cliente. Questo non dovrebbe essere comune. È, tuttavia, inevitabile., Proprio come il software orientato agli oggetti ha bisogno di un metodo statico non orientato agli oggetti come main() per iniziare, un grafico oggetto iniettato dipendenza ha bisogno di almeno un (preferibilmente solo un) punto di ingresso per iniziare il tutto.

La costruzione manuale nel metodo principale potrebbe non essere così semplice e potrebbe comportare la chiamata di costruttori, fabbriche o altri modelli di costruzione. Questo può essere abbastanza avanzato e astratto., La linea viene attraversata dall’iniezione manuale delle dipendenze all’iniezione delle dipendenze del framework una volta che il codice di costruzione non è più personalizzato per l’applicazione ed è invece universale.

Framework come Spring possono costruire questi stessi oggetti e collegarli insieme prima di restituire un riferimento al client. Tutte le menzioni dell’esempio concretoil servizio può essere spostato dal codice ai dati di configurazione.

Framework come Spring consentono di esternalizzare i dettagli dell’assembly nei file di configurazione.Questo codice (sopra) costruisce oggetti e li collega insieme in base ai Bean.xml (sotto)., ExampleService è ancora costruito anche se è menzionato solo di seguito. Un grafico oggetto lungo e complesso può essere definito in questo modo e l’unica classe menzionata nel codice sarebbe quella con il metodo del punto di ingresso, che in questo caso è greet().

Nell’esempio sopra il Cliente e il Servizio non hanno dovuto subire alcuna modifica da fornire entro la primavera. Sono autorizzati a rimanere semplici POJO. Questo mostra come Spring può connettere servizi e clienti che sono completamente ignoranti della sua esistenza. Questo non si può dire se le annotazioni di primavera sono state aggiunte alle classi., Mantenendo le annotazioni e le chiamate specifiche di Spring da diffondere tra molte classi, il sistema rimane solo vagamente dipendente da Spring. Questo può essere importante se il sistema intende sopravvivere primavera.

La scelta di mantenere puro POJOs non viene senza costi. Piuttosto che spendere lo sforzo di sviluppare e mantenere file di configurazione complessi, è possibile utilizzare semplicemente le annotazioni per contrassegnare le classi e lasciare che Spring faccia il resto del lavoro. La risoluzione delle dipendenze può essere semplice se seguono una convenzione come la corrispondenza per tipo o per nome. Questa è la scelta della convenzione rispetto alla configurazione., È anche discutibile che, quando si esegue il refactoring su un altro framework, la rimozione di annotazioni specifiche del framework sarebbe una parte banale dell’attività e molte annotazioni di iniezione sono ora standardizzate.

@Componentpublic class ExampleService { public String getName() { return "World!"; }}

Assembly comparisonEdit

Le diverse implementazioni degli iniettori (fabbriche, localizzatori di servizi e contenitori di iniezione delle dipendenze) non sono così diverse per quanto riguarda l’iniezione delle dipendenze. Ciò che fa la differenza è dove sono autorizzati ad essere utilizzati., Spostare le chiamate a una fabbrica o un localizzatore di servizi dal client e in main e improvvisamente main rende un contenitore di iniezione di dipendenza abbastanza buono.

Spostando tutta la conoscenza dell’iniettore fuori, un client pulito, privo di conoscenza del mondo esterno, è lasciato alle spalle. Tuttavia, qualsiasi oggetto che utilizza altri oggetti può essere considerato un client. L’oggetto che contiene main non fa eccezione. Questo oggetto principale non sta usando l’iniezione di dipendenza. In realtà sta usando il modello di service locator. Questo non può essere evitato perché la scelta delle implementazioni del servizio deve essere fatta da qualche parte.,

L’esternalizzazione delle dipendenze in file di configurazione non cambia questo fatto. Ciò che rende questa realtà parte di un buon design è che il service locator non è diffuso in tutta la base di codice. È limitato a un posto per applicazione. Ciò lascia il resto della base di codice libero di utilizzare l’iniezione di dipendenza per rendere i client puliti.

Dependency Injection PatternEdit

Gli esempi fino ad ora sono stati esempi troppo semplici sulla costruzione di una stringa., Tuttavia, il modello di iniezione delle dipendenze è più utile quando si costruisce un grafico oggetto in cui gli oggetti comunicano tramite messaggi. Gli oggetti costruiti in main dureranno per tutta la vita del programma. Il modello tipico consiste nel costruire il grafico e quindi chiamare un metodo su un oggetto per inviare il flusso di controllo nel grafico dell’oggetto. Proprio come main è il punto di ingresso al codice statico, questo metodo è il punto di ingresso al codice non statico delle applicazioni.,

AngularJS exampleEdit

Nel framework AngularJS, ci sono solo tre modi in cui un componente (oggetto o funzione) può accedere direttamente alle sue dipendenze:

  1. Il componente può creare la dipendenza, in genere utilizzando l’operatorenew.
  2. Il componente può cercare la dipendenza, facendo riferimento a una variabile globale.
  3. Il componente può avere la dipendenza passata ad esso dove è necessario.

Le prime due opzioni di creazione o ricerca delle dipendenze non sono ottimali perché codificano duramente la dipendenza dal componente., Ciò rende difficile, se non impossibile, modificare le dipendenze. Ciò è particolarmente problematico nei test, dove è spesso auspicabile fornire dipendenze simulate per l’isolamento dei test.

La terza opzione è la più praticabile, poiché rimuove la responsabilità di localizzare la dipendenza dal componente. La dipendenza viene semplicemente consegnata al componente.

Nell’esempio precedente, SomeClass non si occupa di creare o localizzare la dipendenza greeter, viene semplicemente consegnato il greeter quando viene istanziato.,

Questo è desiderabile, ma mette la responsabilità di entrare in possesso della dipendenza dal codice che costruisceSomeClass.

Per gestire la responsabilità della creazione delle dipendenze, ogni applicazione AngularJS ha un iniettore. L’iniettore è un localizzatore di servizio responsabile della costruzione e della ricerca delle dipendenze.

Ecco un esempio di utilizzo del servizio iniettore:

Creare un nuovo iniettore in grado di fornire componenti definiti nel modulomyModule e richiedere il nostro servizio greeter dall’iniettore., (Questo di solito viene fatto automaticamente dal bootstrap AngularJS).

var injector = angular.injector();var greeter = injector.get('greeter');

La richiesta di dipendenze risolve il problema della codifica difficile, ma significa anche che l’iniettore deve essere passato in tutta l’applicazione. Il passaggio dell’iniettore infrange la legge di Demetra., Per rimediare a questo, usiamo una notazione dichiarativa nel nostro template HTML, per mano la responsabilità della creazione di componenti per l’iniettore, come in questo esempio:

<div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button></div>
function MyController($scope, greeter) { $scope.sayHello = function() { greeter.greet('Hello World'); };}

Quando AngularJS compila il HTML, processi ng-controller direttiva, che a sua volta chiede l’iniettore per creare un’istanza del controller e le sue dipendenze.

injector.instantiate(MyController);

Questo è tutto fatto dietro le quinte., Poiché ng-controller rinvia all’iniettore per istanziare la classe, può soddisfare tutte le dipendenze di MyController senza che il controller sappia mai dell’iniettore. Il codice dell’applicazione dichiara semplicemente le dipendenze di cui ha bisogno, senza dover gestire l’iniettore. Questa configurazione non infrange la legge di Demetra.

C#Modifica

Esempio di iniezione del costruttore, iniezione del setter e iniezione dell’interfaccia su c #

Articles

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *