Jau buvo paskelbta keletas konkrečių klausimų apie priklausomybių injekciją, pavyzdžiui, kada ją naudoti ir kokie karkasai jai skirti. Tačiau,
**Kas yra priklausomybių injekcija ir kada (kodėl) ją reikėtų ar nereikėtų naudoti?
Geriausias apibrėžimas, kurį iki šiol radau, yra Jameso Shore'o apibrėžimas:
"Priklausomybės injekcija" yra 25 dolerių vertės terminas, skirtas 5 centų sąvokai. [...]
Priklausomybės įšvirkštimas reiškia, kad suteikiama objektui savo egzemplioriaus kintamuosius. [...].
Yra Martino Fowlerio straipsnis, kuris taip pat gali būti naudingas.
Priklausomybių injekcija iš esmės yra objektų, kurių reikia objektui (jo priklausomybių), pateikimas, užuot pačiam juos kūrus. Tai labai naudingas testavimo metodas, nes priklausomybės gali būti išjuokiamos arba išstumiamos.
Priklausomybes į objektus galima įterpti įvairiais būdais (pvz., konstruktoriaus įterpimas arba setterio įterpimas). Tam netgi galima naudoti specializuotas priklausomybių injekcijos sistemas (pvz., "Spring"), tačiau jos tikrai nėra būtinos. Jums nereikia tų karkasų, kad galėtumėte taikyti priklausomybių injekciją. Aiškus objektų (priklausomybių) įkūnijimas ir perdavimas yra toks pat geras įšvirkštimas, kaip ir įšvirkštimas pagal karkasą.
Atsilikimo injekcija - tai priklausomybės perdavimas kitiems objektams arba karkasui ("priklausomybės injektorius").
Priklausomybės injekcija palengvina testavimą. Priklausomybės įšvirkštimas gali būti atliekamas per konstruktorių.
SomeClass()
turi tokį konstruktorių:
public SomeClass() {
myObject = Factory.getObject();
}
Problema:
Jei myObject
apima sudėtingas užduotis, tokias kaip prieiga prie disko ar tinklo, sunku atlikti SomeClass()
vienetinį testavimą. Programuotojams tenka pašiepti myObject
ir gali perimti gamyklos iškvietimą.
Alternatyvus sprendimas:
myObject
pateikimas kaip konstruktoriaus argumentaspublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
gali būti perduodamas tiesiogiai, o tai palengvina testavimą.
Be priklausomybių injekcijos sunkiau izoliuoti komponentus atliekant vienetų testavimą.
2013 m., kai rašiau šį atsakymą, tai buvo pagrindinė tema Google testavimo tinklaraštyje. Man tai tebėra didžiausias privalumas, nes programuotojams ne visada reikia papildomo lankstumo projektuojant paleidimo laiką (pavyzdžiui, paslaugų lokatoriaus ar panašių modelių atveju). Programuotojams dažnai reikia izoliuoti klases testavimo metu.
Priklausomybės įterpimas - tai praktika, kai objektai projektuojami taip, kad jie gautų objektų egzempliorius iš kitų kodo dalių, užuot kūrę juos iš vidaus. Tai reiškia, kad bet kuris objektas, įgyvendinantis objekto reikalaujamą sąsają, gali būti pakeistas nekeičiant kodo, o tai supaprastina testavimą ir pagerina atsiejimą.
Pavyzdžiui, panagrinėkite šias klases:
kalba-visi: 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 ) { ... }
}
Šiame pavyzdyje PersonService::addManager
ir PersonService::removeManager
realizacijai reikia GroupMembershipService
egzemplioriaus, kad ji galėtų atlikti savo darbą. Be priklausomybės įterpimo tradicinis būdas tai padaryti būtų PersonService
konstruktoriuje sukurti naują GroupMembershipService
ir naudoti šio egzemplioriaus atributą abiejose funkcijose. Tačiau jei GroupMembershipService
konstruktorius turi daugybę reikalingų dalykų arba, dar blogiau, yra inicializavimo "setters", kuriuos reikia iškviesti GroupMembershipService
, kodas auga gana greitai, o PersonService
dabar priklauso ne tik nuo GroupMembershipService
, bet ir nuo visko, nuo ko priklauso GroupMembershipService
. Be to, sąsaja su GroupMembershipService
yra kietai užkoduota PersonService
, o tai reiškia, kad negalite sukurti GroupMembershipService
testavimo tikslais arba naudoti strategijos šablono skirtingose savo programos dalyse.
Naudodami priklausomybės injekciją, užuot instancavę GroupMembershipService
savo PersonService
viduje, turėtumėte arba perduoti ją PersonService
konstruktoriui, arba pridėti savybę (getter ir setter), kad nustatytumėte vietinį jos egzempliorių. Tai reiškia, kad jūsų PersonService
nebeturi rūpintis, kaip sukurti GroupMembershipService
, ji tiesiog priima jai suteiktas paslaugas ir dirba su jomis. Tai taip pat reiškia, kad bet kas, kas yra GroupMembershipService
poklasis arba įgyvendina GroupMembershipService
sąsają, gali būti "įšvirkščiama" į PersonService
, ir PersonService
neturi žinoti apie šį pakeitimą.