Objavljenih je bilo že več vprašanj o vbrizgavanju odvisnosti, na primer, kdaj ga uporabiti in katera ogrodja so na voljo za to. Vendar,
Kaj je vbrizgavanje odvisnosti in kdaj in zakaj ga je treba ali ne smemo uporabljati?
Najboljša definicija, ki sem jo do zdaj našel, je definicija Jamesa Shorea:
"Dependency Injection" je 25 dolarjev vreden izraz za koncept za 5 centov. [...] Vbrizgavanje odvisnosti pomeni dajanje objektu svoje spremenljivke primera. [...].
Obstaja članek Martina Fowlerja, ki se lahko izkaže tudi za koristnega.
Vbrizgavanje odvisnosti je v bistvu zagotavljanje predmetov, ki jih objekt potrebuje (njegove odvisnosti), namesto da bi jih objekt zgradil sam. To je zelo uporabna tehnika za testiranje, saj omogoča, da se odvisnosti zasmehujejo ali izrivajo.
Odvisnosti je mogoče v objekte vbrizgati na več načinov (na primer z vbrizgavanjem konstruktorjev ali setterjev). Za to lahko uporabimo celo specializirana ogrodja za vbrizgavanje odvisnosti (npr. Spring), ki pa vsekakor niso potrebna. Za vbrizgavanje odvisnosti ne potrebujete teh ogrodij. Izrecno uvajanje in posredovanje objektov (odvisnosti) je prav tako dobro vbrizgavanje kot vbrizgavanje z ogrodjem.
Vključitev odvisnosti je posredovanje odvisnosti drugim objektom ali okvirju (injektor odvisnosti).
Vbrizgavanje odvisnosti olajša testiranje. Vbrizgavanje se lahko izvede prek konstruktorja.
Konstruktor SomeClass()
je naslednji:
public SomeClass() {
myObject = Factory.getObject();
}
Problem:
Če myObject
vključuje zapletena opravila, kot je dostop do diska ali omrežja, je težko opraviti test enote za SomeClass()
. Programerji morajo zasmehovati myObject
in lahko prekinejo tovarniški klic.
Alternativna rešitev:
myObject
kot argumenta konstruktorjupublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
lahko posredujete neposredno, kar olajša testiranje.
Brez vbrizgavanja odvisnosti je pri testiranju enot težje izolirati komponente.
Leta 2013, ko sem napisal ta odgovor, je bila to glavna tema na Googlovem blogu o testiranju. Zame ostaja največja prednost, saj programerji ne potrebujejo vedno dodatne prilagodljivosti pri načrtovanju izvedbe (na primer za lociranje storitev ali podobne vzorce). Programerji morajo med testiranjem pogosto izolirati razrede.
Vbrizgavanje odvisnosti je praksa, pri kateri so objekti zasnovani tako, da prejemajo primerke objektov iz drugih delov kode, namesto da bi jih zgradili interno. To pomeni, da lahko katerikoli objekt, ki implementira vmesnik, ki ga objekt zahteva, zamenjamo brez spreminjanja kode, kar poenostavi testiranje in izboljša ločevanje.
Na primer, upoštevajte te razrede:
jezik-all: lang-csh -->
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
V tem primeru bi implementacija PersonService::addManager
in PersonService::removeManager
za svoje delo potrebovala primerek storitve GroupMembershipService
. Brez vpeljave odvisnosti bi to tradicionalno storili tako, da bi v konstruktorju PersonService
instancirali novo GroupMembershipService
in uporabili atribut te instance v obeh funkcijah. Če pa konstruktor GroupMembershipService
zahteva več stvari ali, kar je še slabše, če obstajajo nekateri inicializacijski "setters", ki jih je treba poklicati na GroupMembershipService
, koda precej hitro naraste in PersonService
zdaj ni odvisna samo od GroupMembershipService
, temveč tudi od vsega drugega, od česar je odvisna GroupMembershipService
. Poleg tega je povezava s storitvijo GroupMembershipService
trdno zakodirana v storitvi PersonService
, kar pomeni, da ne morete ustvariti navidezne storitve GroupMembershipService
za namene testiranja ali za uporabo vzorca strategije v različnih delih vaše aplikacije.
Z vbrizgavanjem odvisnosti bi namesto instanciranja storitve GroupMembershipService
v storitvi PersonService
to storitev posredovali konstruktorju storitve PersonService
ali dodali lastnost (getter in setter) za nastavitev lokalne instance te storitve. To pomeni, da vaši storitvi PersonService
ni več treba skrbeti, kako ustvariti storitev GroupMembershipService
, temveč samo sprejme tiste, ki so ji dane, in dela z njimi. To tudi pomeni, da lahko karkoli, kar je podrazred GroupMembershipService
ali implementira vmesnik GroupMembershipService
, "injiciramo" v PersonService
, in PersonService
ni treba vedeti za to spremembo.