Come viene utilizzata un'interfaccia in Dependency Injection?

0

Sto lavorando su una semplice libreria DI, e sul tema dell'utilizzo dei servizi attraverso le interfacce, non riesco a trovare l'utilità. Forse è perché non comprendo pienamente o correttamente come un'interfaccia dovrebbe essere usata durante il processo di iniezione delle dipendenze.

Ecco cosa penso (penso):

DI consiste principalmente di clienti e servizi. Le interfacce consentono all'iniettore di comunicare al cliente quali variabili e metodi sono disponibili attraverso un servizio specifico.

Se non è sbagliato:

Mi sembra che l'interfaccia possa passare nomi di proprietà disponibili e che tipo siano, quindi il mio cliente può sapere se sono funzioni o valori, ma ciò non sembra utile dato che posso farlo con o senza il interfaccia. Se i metodi / valori vengono utilizzati tramite un'interfaccia o tramite il servizio inserito, posso conoscerne i tipi perché posso utilizzare l'% operando typeof (JavaScript) indipendentemente dal tipo di proprietà che sto per utilizzare.

Quindi penso che non debba avere una comprensione adeguata di ciò che le interfacce dovrebbero fare. In che modo vengono utilizzate le interfacce nell'iniezione delle dipendenze?

    
posta Viziionary 02.10.2015 - 16:14
fonte

4 risposte

5

Il vantaggio non è che l'interfaccia dichiara i membri e i metodi di cui ha bisogno un cliente - che è esattamente ciò che una classe concreta può fare. Il vantaggio è che una classe concreta può implementare un'interfaccia, che consente di sostituire la dipendenza in fase di esecuzione. Per i test, è possibile passare in una simulazione che implementa la stessa interfaccia, ma senza tutto il sovraccarico della solita classe (forse le chiamate al database). La tua classe dipende solo da un'interfaccia, ma l'implementazione è lasciata al contenitore DI / IoC da iniettare.

    
risposta data 02.10.2015 - 16:28
fonte
4

Ci sono almeno tre ragioni per cui potresti voler iniettare un'interfaccia invece di una classe concreta:

  1. Hai più implementazioni di quell'interfaccia e vuoi che il contenitore ne selezioni una in fase di esecuzione.
  2. Vuoi scrivere test unitari che isolano fuori il servizio iniettato, e quindi vuoi fornire un'implementazione simulata per il test (questo è probabilmente lo stesso di # 1, dal momento che un finto è solo un altro implementazione, anche se fornita da un framework di simulazione).
  3. Hai una qualche forma di Onion Architecture, dove la tua classe consumante è in un assembly non ha conoscenza dell'assembly dell'implementazione, ma ha conoscenza di un assembly comune (dove risiede l'interfaccia).

È altrimenti perfettamente accettabile iniettare tipi concreti. In realtà ho chiesto di iniettare tipi concreti su SO qui .

    
risposta data 02.10.2015 - 16:53
fonte
1

Ecco un esempio di un progetto angularjs che viene effettivamente utilizzato da grandi clienti con cui probabilmente lavori.

Abbiamo domande che vengono lette da json (tecnicamente molti iniziano come xml e subiscono un processo di conversione). I vari tipi di domanda hanno tutti cose diverse che devono essere aggiunti a loro in base a ciò che i nostri clienti vogliono sapere. Potremmo mettere tutta quella "roba" in XML / json, ma questo è un sacco di spese generali e molto incline agli errori. Quindi, invece, creiamo una classe "factory" per ogni tipo di domanda che sappia prendere alcuni dati di inizializzazione e una domanda di valore di JSON e aggiungere tutte quelle cose ad essa. Poi abbiamo un'altra classe la cui responsabilità è di ottenere il file JSON, cercare di vedere che tipo di domanda è, chiamare $ injector. $ Get, recuperare la factory, inizializzare la factory e poi chiamare il metodo createQuestion () su ogni factory . Questo ci consente di offrire una flessibilità infinita sui tipi di domande che possiamo analizzare - per aggiungerne un'altra, basta registrare un'altra fabbrica con Angular che sa cosa fare e nient'altro deve preoccuparsi.

Si noti che in realtà non fornisco o implemento un'interfaccia. Non sto lavorando in TypeScript, quindi AFAIK non esiste un modo reale per farlo in un semplice JavaScript. La presunzione è se l'iniettore trova qualcosa chiamato questionTypeFactory, ha un metodo init e createQuestion.

Usiamo lo stesso modello per sincronizzare le cose con l'audio. I nostri file JSON che definiscono una collezione di "demoSteps" e ogni fase demo hanno una proprietà "azione". Quando il framework audio chiama il callback, il callback si agita intorno a $ injector per actionNameCommand, passa alcuni dati di inizializzazione e chiama il suo metodo execute (). Ancora una volta, non c'è un'interfaccia definita, si presume semplicemente che se trovi qualcosa chiamato actionNameCommand nessuno lo metta lì senza scriverlo correttamente. E abbiamo dei template per il resharper che lo rendono banale.

    
risposta data 03.10.2015 - 00:40
fonte
1

Dipendenza Iniezione sta consegnando a un pezzo di software le sue dipendenze in fase di runtime, invece di codificare quel pezzo di software in modo tale da avere una conoscenza intima del tipo preciso legata staticamente, hard-coded e istanza di ciascuna delle sue dipendenze.

Ad esempio, questo è senza DI:

function prepareToDrink()
{
     TheOneAndOnlyGloballyAvailableHeinekenBottle.open();
}

E questo è con DI:

function prepareToDrink( Bottle anyBottle )
{
     anyBottle.open(); //also works with that Budweisser bottle. Also with champagne.
}

Come puoi vedere, non solo funzionerà con qualsiasi birra Bottle in casa, ma anche con altri tipi di bottiglie, assumendo che le altre bottiglie siano derivate dalla stessa classe base Bottle .

La definizione che hai trovato sceglie di chiamare il "pezzo di software" un "client" e la dipendenza un "servizio", ma discussioni elaborate di questo tipo possono essere fuorvianti, a volte.

Una dipendenza è davvero tutto ciò di cui il tuo pezzo di software ha bisogno per fare il suo lavoro. (Dipende da questo.) Può essere un oggetto, o può essere un'interfaccia; ma se dovesse essere un oggetto, allora quell'oggetto dovrebbe essere di una classe specifica, o una classe più derivata da essa, e questo sarebbe un po 'limitante. Preferiamo utilizzare le interfacce in modo che possiamo passare qualsiasi classe che ci piace, senza che debba essere derivata da una classe specifica.

Quindi, per esempio, se vogliamo anche essere in grado di aprire lattine di birra, ma Can non è derivato da Bottle , possiamo avere sia Can che Bottle implementare l'interfaccia Openable , nel qual caso possiamo fare questo:

function prepareToDrink( Openable anythingThatSupportsOpening )
{
    anythingThatSupportsOpening.open(); //opens bottles; also opens cans.
}
    
risposta data 03.10.2015 - 00:03
fonte

Leggi altre domande sui tag