A számítógép-programozásban a dependency injection egy technika, aminek lényege, hogy egy objektum más objektumok függőségeit elégíti ki. A függőséget felhasználó objektum szolgáltatást nyújt, az injekció pedig ennek a függőségnek az átadása a kliens részére. A szolgáltatás a kliens állapotának része.[1] A minta alapkövetelménye a szolgáltatás kliensnek való átadása ahelyett, hogy a szolgáltató objektumot a kliens hozná létre.
A szolgáltató osztály szempontjából ez azt jelenti, hogy a kliens nem hívhat rajta konstruktort, vagy statikus metódust. Paramétereit más osztályoktól kapja, azok állítják be. A függőséget előállítja valaki más, például a kontextus vagy a konténer problémája lesz.
A minta célja, hogy annyira leválassza a szolgáltató objektumot a kliensről, hogy ha kicserélik, akkor ne kelljen módosítani a klienst.
A dependency injection a vezérlés megfordításának egyik formája. Ahelyett, hogy az alacsony szintű kód hívná a magas szintűt, a magas szintű fogadja az alacsony szintűt, amit hívhat. Ez megfordítja a procedurális programozás szokásos vezérlési mintáját.
Ahogy a vezérlés megfordításának többi formája, a dependency injection alkalmazza a felelősség megfordításának elvét. A kliens külső kódnak delegálja függőségeinek létrehozását az injektornak, amit azonban nem hívhat.[2] Fordítva, az injektor hívja a klienst, és adja át neki az objektumot. A kliensnek nem kell tudnia, hogyan kell létrehozni a szolgáltatót, és nem kell tudnia az injektor kódról sem. Csak a szolgáltató interfészét kell ismernie, mert ez definiálja, hogyan hívhatja meg a szolgáltatásokat. Ez elkülöníti egymástól a létrehozás és a használat felelősségét.
A kliens három különböző módon fogadhatja a szolgáltatásokat: szetter, interfész és konstruktor alapú injekcióban. A szetter és a konstruktor injekció abban különbözik, hogy mikor lehet őket használni. Ezektől az interfész alapú injekció abban különbözik, hogy a szolgáltató objektum ellenőrizheti injekcióját. Mindezek megkövetelik, hogy egy külön kód, az injektor hozza létre a kapcsolatot a másik két elem között.[3]
Céljai
A dependency injection a következő problémákat oldja meg:[4]
Hogyan lehet az alkalmazás független attól, hogyan hozzák létre az objektumait?
Hogyan lehet egy objektum független attól, hogy az általa megkövetelt objektumok hogyan jönnek létre?
Hogyan lehet elérni azt, hogy minden objektum tulajdonságait külön konfigurációs fájlban lehessen megadni?
Hogyan támogathat az alkalmazás több különböző konfigurációt?
Az objektumok létrehozása a kliens osztályban merevvé teszi a kódot, mivel ezután az objektum csak a megadott osztályú másik objektumot hozhatja létre és használhatja. Lehetetlenné válik, hogy a tartalmazott objektumot kicseréljék a kliens módosítása nélkül. Ez megnehezíti az újrahasználását és a tesztelést is, mert mókolás előtt és után vigyázni kell, hogy most melyik objektumot használjuk, és plusz hibalehetőség adódik.
A dependency injection minta megoldást nyújt erre a problémára:
Injektor osztály létrehozása, ami létrehozza és injektálja az objektumokat
Az osztály a közvetlen létrehozás helyett az injektortól kapja az objektumokat.
Az osztály így függetlenné válik attól, hogyan hozzák létre a szükséges objektumait, és hogy melyik konkrét osztály tagját használja. A dependency injection miatt az objektumnak nem kell a többi létrehozási mintának (absztrakt gyár, gyártó minta, gyártó metódus, ...) delegálnia az objektum létrehozását. Ez leegyszerűsíti az osztályokat, a megvalósítást, változtatást, tesztelést és újrafelhasználást.[5] design pattern.
Áttekintése
Dependency injection ötéveseknek
Ha kimész a konyhába, és előveszel magadnak valamit enni, akkor problémát okozhatsz. Nyitva hagyhatod az ajtót, elővehetsz valamit, amit a szüleid nem akarnak, hogy megegyél. Olyat kereshetsz, ami nincs bent, vagy elővehetsz valamit, ami meg van romolva.
Ehelyett, ha azt mondod: "Szeretnék valamit enni és inni", akkor biztos lehetsz abban, hogy lesz mit enned és innod, és nem okozol problémát.
A dependency injection a függőség megfordítása és az egyértelmű felelősség elvét követve[6][9] meglazítja az objektumok közötti csatolást azáltal,[10] hogy elkülöníti a létrehozást és a használatot. Vele szemben áll a szolgáltatáslokátor, ami megengedi a klienseknek, hogy tudomásuk legyen a rendszer egy részéről, hogy megtalálják függőségeiket.
Alapegysége az injekció, ami a paraméterátadáshoz hasonlóan működik.[11] A paraméterátadásra hivatkozva nyilvánvalóvá válik, hogy ez azért van, hogy a klienst elszigetelje a részletektől.
Az injekció azt is jelenti, hogy az átadás független a klienstől, és független attól, hogy hogyan valósul meg, érték vagy referencia szerint.
A dependency injection elemei:
Szolgáltató objektum
Kliens
Interfész, amin keresztül a kliens a szolgáltató objektumot látja
Injektor, ami létrehozza, beállítja és injektálja a szolgáltató objektumot
A kliens egy objektum, ami igénybe vesz egy másik objektumot. A szolgáltató objektum az, ami szolgáltatást nyújt egy kliens részére. Egy objektum lehet egyszer kliens, másszor szolgáltató objektum.
Az interfész a kliens által elvárt típus. A kliens csak azt láthatja, amit az interfész előír, a szolgáltató objektum többi nyilvános vagy félnyilvános műveletét sem hívhatja. Az interfész lehet interfész, absztrakt osztály vagy konkrét osztály is. Ez utóbbi azonban megsérti a függőség megfordításának elvét,[12] és feláldozza a dinamikus kapcsolatot, ami megkönnyítené a tesztelést. A kliens nem tudhatja, hogy ez pontosan mi, nem tekintheti konkrétnak, nem származtathat belőle vagy hozhat létre saját maga elemet belőle.
A kliens nem ismerheti a szolgáltató objektum konkrét megvalósítását, csak az interfészét és az API-t. A szolgáltató objektum változásai egészen addig nem érintik a klienst, amíg az interfész nem változik. Ha az interfész valódi interfészből osztály lesz vagy megfordítva, akkor a klienst újra kell fordítani.[13] Ez fontos, ha a klienst és a szolgáltató objektumot külön adják ki, például ha egy plugint kell használni.
Az injektor odaadja a szolgáltató objektumot a kliensnek. Gyakran ő is hozza létre és állítja be. Az injektor összekapcsolhat egy bonyolult objektumgráfot azzal, hogy egy objektum hol kliens, hol szolgáltató objektum. Alkothatja több különböző osztályú objektum, de a kliens nem lehet köztük. Az injektorra más neveken is hivatkoznak: assembler, szolgáltató, konténer, gyár, építő, spring, konstrukciós kód vagy main.
A minta alkalmazható architekturális szinten úgy, hogy minden összetett objektum így veszi át részobjektumait. A dependency injection keretrendszer akár be is tilthatja a new kulcsszó használatát, vagy csak érték objektumok létrehozását engedélyezheti.[14][15][16][17]
Taxonómia
A vezérlés megfordítása általánosabb, mint a dependency injection. Az előbbi a Hollywood-elven alapul: Ne hívj, mi hívunk téged. Egy másik példa a sablonfüggvény programtervezési minta, ahol a polimorfizmust öröklődéssel érjük el.[18]
A stratégia programtervezési mintához hasonlóan a dependency injection kompozícióval valósítja meg a vezérlés megfordítását. De míg az az objektum élete alatt is fenntartja a példány kicserélődésének lehetőségét, addig ez többnyire egyetlen példányt használ a kliens életideje alatt.[19] Így a polimorfizmus a kompozícióval és delegációval valósul meg.
A dependency injection meghagyja a kliensnek a rugalmasságát a konfiguráció tekintetében. Csak a kliens viselkedése rögzített. A kliens bármivel működik, ami támogatja a kliens által elvárt interfészt.
A minta használható arra, hogy a rendszer konfigurációjának részletei konfigurációs fájlokban legyenek megadva, Különböző környezetekhez, helyzetekhez különböző konfigurációs fájlokban különböző konfigurációkat lehet megadni.
Mivel nem követeli meg a kliens kód megváltoztatását, alkalmas arra, hogy legacy kódot támogasson. Eredménye az, hogy a kliensek függetlenebbek, ezért egyszerűbb egységteszteket végezni. Dependency injection alkalmazásakor gyakran ez az első előny, amit észrevesznek.
A minta lehetővé teszi, sőt megköveteli, hogy a kliensből eltávolítsanak minden tudást, ami az általa használt objektumok konkrét megvalósításának részleteit tartalmazza. Támogatja a karbantarthatóságot és az újrafelhasználhatóságot.[22]
Az alkalmazásobjektumokban levő szószátyár kód (boilerplate) csökkenése, mivel az inicializálást és beállítást külön komponens végzi.[22]
Segíti a független és a párhuzamos fejlesztést. Az egymást használó osztályok szétoszthatók különböző fejlesztők között, mivel csak egy közös interfészt kell ismerniük. Felhasználhatók olyan pluginok, amelyek fejlesztőivel nem lehetett megbeszélni a részleteket.
A minta gyengíti a kapcsolatot a kliens és a szolgáltató objektum között.[23][24]
Hátrányai
A kliensek konfigurációs fájlt követelnek, amikor az alapértelmezett értékeknek maguktól kell értődniük. Ez bosszantó lehet, és hibalehetőséget is jelent.
A létrehozás és a viselkedés elkülönítése miatt több fájlban kell nyomon kvetelni a rendszer viselkedését. Ez bonyolultabbá teszi a hibák helyének megtalálását.
A minta rendszerint a fejlesztés irányát és sorrendjét is megszabja. Ha egy objektum az egységtesztek során megfelelőnek látszik, akkor még azt is igazolni kell, hogy az injekció valóban működik.
A bonyolultság a fájlokból átkerül a fájlok rendszerébe, ami nem mindig kívánatos, vagy kezelhető könnyen.[25]
A dependency injection arra csábíthatja a fejlesztőket, hogy függjenek egy keretrendszertől.[25][26][27]
Szerkezete
A fenti UML osztálydiagramon a Client osztály nem példányosítja közvetlenül a ServiceA1 és ServiceB1 osztályokat. Neki ServiceA és ServiceB típusú objektumok kellenek, amiket az Injector hoz létre, és injektál a Clientbe. Ezzel a Client függetlenné válik attól, hogyan hozzák létre az általa használt objektumokat, így a konkrét osztályok helyett csak azok általánosításáról tud.
Az UML folyamatdiagram a futásidejű történéseket mutatja: Az Injector létrehozza a ServiceA1 és ServiceB1 osztályú objektumokat. Ezután az Injector létrehozza a Clientet is, és odaadja neki a ServiceA és ServiceB osztályú objektumokat.
Példa
Közvetlen példányosítás
A következő Java példában a Client osztály egyik mezőjében tartalmaz egy Service osztályú objektumot, amit a Client konstruktora inicializál. A Client dönti el, hogy melyik megvalósítást használja, és ellenőrzi a konstrukciós folyamatot. Ekkor a Client erősen függ a ServiceExample osztálytól.
// An example without dependency injectionpublicclassClient{// Internal reference to the service used by this clientprivateServiceExampleservice;// ConstructorClient(){// Specify a specific implementation in the constructor instead of using dependency injectionservice=newServiceExample();}// Method within this client that uses the servicespublicStringgreet(){return"Hello "+service.getName();}}
Ezzel szemben a dependency injection csak az inicializációt engedi meg, az explicit példányosítást nem.
A dependency injection fajtái
Az osztály legalább háromféleképpen szerezhet referenciát egy külső modulra:[29]
konstruktor injekció: a szolgáltató objektumokról a konstruktor gondoskodik.
szetter injekció: a szolgáltató objektumot a szetter igényli
interfész injekció: a szolgáltató objektumnak van injektáló metódusa, ami injektálja bármely kliensbe, amit megkap paraméterként. A klienseknek meg kell valósítaniuk egy interfészt, ami előír egy szetter metódust, ami fogadja az injekciót.
Keretrendszerek lehetővé tehetnek más formájú dependency injectionöket is.[30]
Teszt keretrendszerekben még azt sem követelik meg, hogy a kliens aktívan fogadja a dependency injectiont, ezzel a legacy kód is tesztelhető. Speciálisan, a tesztek használhatnak reflexiót is, amivel hozzáférhetnek a privát adattagokhoz is, így értékadással fogadhatnak injekciót.[31]
A vezérlés megfordítása nem távolítja el teljesen a függőséget, csak helyettesíti egy másikkal. Ökölszabály, hogy ha egy programozó a kliens kódját látva azonosítani tudja a keretrendszert, akkor a kliens erősen függ a keretrendszertől.
Konstruktor injekció
A kliens konstruktora paraméterként veszi át a szolgáltató objektumot:
// ConstructorClient(Serviceservice){// Save the reference to the passed-in service inside this clientthis.service=service;}
Szetter injekció
A kliens szetterben veszi át a szolgáltató objektumot, mint paramétert:
// Setter methodpublicvoidsetService(Serviceservice){// Save the reference to the passed-in service inside this clientthis.service=service;}
Interfész injekció
A kliens csak az interfészt adja meg a szolgáltató objektumokat beállító függvények számára. Meghatározhatja, hogyan beszélhet az injektor a klienshez injektáláskor.
// Service setter interface.publicinterfaceServiceSetter{publicvoidsetService(Serviceservice);}// Client classpublicclassClientimplementsServiceSetter{// Internal reference to the service used by this client.privateServiceservice;// Set the service that this client is to use.@OverridepublicvoidsetService(Serviceservice){this.service=service;}}
Konstruktor injekció összehasonlítás
A konstruktor injekció előnyben részesítendő, ha az összes szolgáltató objektum megkonstruálható a kliensnél korábban. Ezzel biztosítható, hogy a kliens állapota mindig érvényes legyen. Ezzel szemben hiányzik az a rugalmassága, hogy később meg lehessen változtatni a szolgáltató objektumait. Ez lehet az első lépés afelé, hogy a kliens megváltoztathatatlan legyen, ezzel szálbiztossá váljon.
// ConstructorClient(Serviceservice,ServiceotherService){if(service==null){thrownewInvalidParameterException("service must not be null");}if(otherService==null){thrownewInvalidParameterException("otherService must not be null");}// Save the service references inside this clientthis.service=service;this.otherService=otherService;}
Szetter injekció összehasonlítás
A kliensnek minden szolgáltató objektumához szetter kell, így azok bármikor megváltoztathatók. Ez rugalmasságot biztosít, viszont nehezebb biztosítani, hogy mindig mindegyik szolgáltató objektum rendelkezésre álljon.
// Set the service to be used by this clientpublicvoidsetService(Serviceservice){if(service==null){thrownewInvalidParameterException("service must not be null");}this.service=service;}// Set the other service to be used by this clientpublicvoidsetOtherService(ServiceotherService){if(otherService==null){thrownewInvalidParameterException("otherService must not be null");}this.otherService=otherService;}
Mivel az injekciók függetlenek egymástól, sosem lehet tudni, hogy az injektor végzett-e a beállítással. Egy szolgáltató objektum null maradhat, ha az injektor nem tudta meghívni a szetterét. Emiatt ellenőrizni kell a klienst, mielőtt további használatba adjuk.
// Set the service to be used by this clientpublicvoidsetService(Serviceservice){this.service=service;}// Set the other service to be used by this clientpublicvoidsetOtherService(ServiceotherService){this.otherService=otherService;}// Check the service references of this clientprivatevoidvalidateState(){if(service==null){thrownewIllegalStateException("service must not be null");}if(otherService==null){thrownewIllegalStateException("otherService must not be null");}}// Method that uses the service referencespublicvoiddoSomething(){validateState();service.doYourThing();otherService.doYourThing();}
Interfész injekció összehasonlítás
Az interfész injekció előnye, hogy a szolgáltató objektumoknak nem kell tudniuk a kliensekről, habár mindig kapnak referenciát az új kliensre, amivel visszahívják a klienst, és átadják magukat referenciaként. Így a szolgáltató objektumok injektorokká válnak. Az injektáló metódust a szolgáltató objektum interfésze írja elő, és akár egyszerű szetter metódus is lehet.
Még mindig szükség van egy mediátorra, ami bemutatja egymásnak a szolgáltató objektumokat és a klienseket. Ez átvesz egy hivatkozást a kliensre, átadja a szetter interfésznek, ami beállítja a szolgáltató objektumot, ezután visszaadja a szolgáltató objektumnak, ami visszaad egy rá mutató referenciát.
Hogy legyen értéke az interfész injekciónak, a szolgáltató objektumnak további műveleteket is kell végeznie. Ez lehet az, hogy gyárként működik, hogy feloldja a további függőségeket, és részleteket vonatkoztat el a fő közvetítőtől. Végezhet referenciaszámlálást, ebből megtudhatja, hány kliens használja.
Ha adatszerkezetben tárolja őket, akkor később injektálhatja őket egy másik példányába.
Összegyűjtési példa
A dependency injection megvalósításának egyik módja az, ha kézzel gyűjtjük össze a szereplőket a mainben:
publicclassInjector{publicstaticvoidmain(String[]args){// Build the dependencies firstServiceservice=newServiceExample();// Inject the service, constructor styleClientclient=newClient(service);// Use the objectsSystem.out.println(client.greet());}}
A fenti példa kézzel hozza létre az objektumgráfot, és meghívja egy ponton, ezzel elindítva a működést. Fontos megjegyezni, hogy ez az injektor nem tiszta, mert az elindítással használ egy általa létrehozott objektumot. Hasonlít a csak konstruáló ServiceExample-hoz, de keveri a konstrukciót és a kliens felhasználását. Habár ez elkerülhetetlen, nem szabad általánosnak lennie. Ahogy az objektumorientált programozásban van nem objektumorientált main metódus, úgy a dependency injection objektumgráfjának is kell belépési pont, ha lehet, akkor csak egy.
A main függvény a létrehozáshoz használhat különféle létrehozási tervmintákat, mint absztrakt gyár, gyártó metódus, építő. Ezek a minták lehetnek absztraktak, ami már elmozdulás a keretrendszer felé, mivel a konstrukciós kód univerzális.[32]
A keretrendszerek, mint a Spring is létrehozhatja ugyanezeket az objektumokat, és összekapcsolhatja őket, mielőtt még referenciát adna a kliensre. Jegyezzük meg, hogy az alábbi kód meg sem említi a ServiceExample-t:
A keretrendszerek azt is lehetővé teszik, hogy a részleteket konfigurációs fájlokba emeljük ki. A fenti kód létrehozza az objektumokat, és a Beans.xml alapján összekapcsolja őket. Létrejön a ServiceExample is, habár csak a konfigurációs fájl említi. A fájlban nagy és összetett objektumgráfot lehet definiálni, és csak a belépési metódust kell explicit hívni, ami ebben az esetben a greet().
A fenti példában a Client és a Service nem ment át olyan a változásokon, amilyeneket a Spring képes nyújtani, egyszerű POJO-k maradtak.[33][34][35] A példa azt mutatja, hogy a Spring képes összekapcsolni egymásról semmit sem tudó objektumokat. Az annotációkkal a rendszer függetlenebbé válik a Springtől,[26] ami fontos, ha egy szép nap a projektmenedzser kitalálja, hogy át kell térni egy másik keretrendszerre, mert az esetleg többet tud.
A POJO-k tisztán tartása nem érhető el költségek nélkül. Konfigurációs fájlok létrehozása helyett annotációk is bevezethetők, és a Spring elvégzi a többit. A függőségek feloldása egyszerű, ha konvenciókat követnek típus vagy név szerinti illeszkedéssel.[36] Ez a konfiguráció fölötti konvencióválasztás. Lehet amellett is érvelni, hogy a keretrendszer cseréjekor a specifikus annotációk eltávolítása egyszerű,[37] és hogy sok injekció annotáció szabványos.[38][39]
importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassInjector{publicstaticvoidmain(String[]args){// Assemble the objectsBeanFactorybeanfactory=newAnnotationConfigApplicationContext(MyConfiguration.class);Clientclient=beanfactory.getBean(Client.class);// Use the objectsSystem.out.println(client.greet());}}
A különböző injektor megvalósítások (gyárak, szolgáltatáslokátorok, dependency injection konténerek) csak annyiban különböznek egymástól, hogy hol használhatók. Ha a gyárat vagy szolgáltatáslokátort nem a kliens hívja, hanem a main, akkor a main kiváló dependency konténerré válik.
Mivel az injektorról való tudás kikerül a kliensből, az tiszta klienssé válik. Azonban bármely objektum, ami objektumokat használ, tekinthető kliensnek, ami alól a main sem kivétel. A main a dependency injection helyett szolgáltatáslokátort, vagy gyárat használ. Ez nem kerülhető el, mert valahol csak dönteni kell, hogy melyik implementációt használjuk.
Az sem változtat ezen, hogy a függőségeket kiszervezzük konfigurációs fájlokba. A jó tervezés egy helyre gyűjti össze a tudást, a szolgáltatáslokátorral csak ez az egy rész áll közvetlen kapcsolatban, a többi kliens tiszta.
AngularJS példa
Az AngularJS keretrendszerben egy komponens háromféleképpen férhet hozzá függőségeihez:
Létrehozza a függőséget, tipikusan a new operátorral.
Globális változóra hivatkozva elkéri.
Mire a függőséget használnia kell, addigra megkapja valahonnan.
Az első két lehetőség nem a legjobb, mivel szorosan összekapcsolja az egyes elemeket. Ez megnehezíti a függőségek módosítását. Különösen problémás ez a tesztek esetén, hogy erre még külön figyelni kell mókolás előtt és utána is.
A harmadik lehetőség már meglazítja a kapcsolatot, mivel a komponensnek már nem kell tudnia, hogy pontosan mit is használ, vagy hol található. A függőséget egyszerűen átadják az objektumnak.
A példában a SomeClass osztálynak nem kell törődnie azzal, hogy létrehozza vagy megkeresse a greeter objektumot, mert példányosításkor megkapja. Ez kívánatos, de a függőségről való tudást áthelyezi abba a kódba, ami elkészíti a SomeClass példányát. A függőségek létrehozására az AngularJS keretrendszer minden alkalmazást injektorral lát el. Az injektor egy szolgáltatáslokátor, ami a függőségek létrehozásáért és kikereséséért felelős. Példa az injektor használatára:
// Provide the wiring information in a modulevarmyModule=angular.module('myModule',[]);// Teach the injector how to build a greeter service. // Notice that greeter is dependent on the $window service. // The greeter service is an object that// contains a greet method.myModule.factory('greeter',function($window){return{greet:function(text){$window.alert(text);}};});
Ez létrehoz egy injektort a myModule objektumai számára. A greeter szolgáltató objektum is lekérhető tőle, amit az AngularJS bootstrap elvégez.
A függőségek lekérése megoldja a kapcsolat lazítását, de azt is jelenti, hogy az injektort a teljes alkalmazásból el kell tudni érni. Ez megsérti a Demeter-elvet, ami a minimális tudás elve. Ez megszüntethető, ha a dokumentumban deklaráljuk, hogy HTML sablonjainkban a létrehozásért az injektor a felelős, mint például:
Ha az AngularJS lefordítja a HTML-t, akkor feldolgozza az ng-controller direktívát is, és megkéri az injektort, hogy hozza létre a vezérlő példányát, és a tőle függő objektumokat.
injector.instantiate(MyController);
Mindezek a színfalak mögött történnek. Jegyezzük meg, hogy azáltal, hogy az ng-controller megkéri az injektort a példányosításra, a vezérlőnek nem kell tudnia az injektor létezéséről. Ez a legjobb kimenetel. Az alkalmazás csak deklarálja a függőségeket, és nem kell törődnie az injektorral, mert automatikusan megkapja őket. Ezzel a Demeter-elvet is betartja.
↑Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley, 87ff. o. (1994). ISBN 0-201-63361-2
↑ abDependency Injection in .NET. Manning Publications, 4. o. (2011. október 1.). ISBN 9781935182504
↑Niko Schwarz, Mircea Lungu, Oscar Nierstrasz, “Seuss: Decoupling responsibilities from static methods for fine-grained configurability”, Journal of Object Technology, Volume 11, no. 1 (April 2012), pp. 3:1-23
↑Morling, Gunnar: Dagger - A new Java dependency injection framework. Dagger - A new Java dependency injection framework - Musings of a Programming Addict , 2012. november 18. (Hozzáférés: 2015. július 18.)
Ez a szócikk részben vagy egészben a Dependency injection című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.
2015 video gameDragon Age: Inquisition – Jaws of HakkonDeveloper(s)BioWarePublisher(s)Electronic ArtsSeriesDragon AgeEngineFrostbite 3Platform(s)Microsoft WindowsXbox OnePlayStation 3PlayStation 4Xbox 360ReleaseWindows, Xbox OneMarch 24, 2015PS3, PS4, Xbox 360May 26, 2015Genre(s)Action role-playingMode(s)Single-player Dragon Age: Inquisition – Jaws of Hakkon is a downloadable content (DLC) pack developed by BioWare and published by Electronic Arts for the 2014 action role-playing video ga...
Pour les articles homonymes, voir 37e régiment. 37e régiment d'infanterie territorial Soldats du régiment en position au nord-est de Saint-Martin (Meurthe-et-Moselle) en août 1916. Création 2 août 1914 Dissolution fin février 1918 Pays France Branche Armée de terre Type Régiment d'infanterie territoriale Rôle Infanterie Garnison Auxerre Guerres Première Guerre mondiale modifier Le 37e régiment d'infanterie territorial est un régiment d'infanterie de l'Armée ...
Este artículo o sección necesita referencias que aparezcan en una publicación acreditada.Este aviso fue puesto el 20 de febrero de 2019. Consejo de Seguridad Pública LocalizaciónPaís Panamá PanamáInformación generalSede Palacio de Las Garzas, Ciudad de PanamáHistoriaFundación 10 de febrero de 1990[editar datos en Wikidata] El Consejo de Seguridad Pública y Defensa Nacional (C.S.P.D.N.) es el principal servicio de inteligencia de Panamá, creado por el decreto N°38 del 1...
Esta página cita fontes, mas que não cobrem todo o conteúdo. Ajude a inserir referências. Conteúdo não verificável pode ser removido.—Encontre fontes: ABW • CAPES • Google (N • L • A) (Julho de 2021) Localização do Condado de Van Wert em Ohio. Esta é a lista de lugares históricos do Condado de Van Wert, Ohio, listados no Registro Nacional de Lugares Históricos. Existem seis propriedades do condado listados no Regist...
Not to be confused with MacGyver (2016 TV series, season 5). Season of television series MacGyverSeason 5The Right Man When Things Go WrongCountry of originUnited StatesNo. of episodes21ReleaseOriginal networkABCOriginal releaseSeptember 18, 1989 (1989-09-18) –April 30, 1990 (1990-04-30)Season chronology← PreviousSeason 4 Next →Season 6 List of episodes The fifth season of MacGyver, an American television series, began September 18, 1989, and ended on April 30, ...
1980 studio album by David Allan CoeI've Got Something to SayStudio album by David Allan CoeReleasedJune 1980Recorded1976StudioColumbia Studios, Pete's Place in NashvilleGenreCountryLength30:47LabelColumbiaProducerBilly SherrillDavid Allan Coe chronology Spectrum VII(1979) I've Got Something to Say(1980) Invictus (Means) Unconquered(1981) Professional ratingsReview scoresSourceRatingAllmusic [1] I've Got Something to Say is an album released by country musician David Allan Coe. It was...
Poem by Lord Byron This article needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed.Find sources: So, we'll go no more a roving – news · newspapers · books · scholar · JSTOR (January 2020) (Learn how and when to remove this template message) So, we'll go no more a roving So we'll go no more a roving So late into th...
German botanist and naturalist (1796–1830) Location of Cape Mertens Karl Heinrich Mertens (Russian: Андрей Карлович Мертенс, 17 May 1796 – 18 September 1830 Kronstadt), was a German botanist and naturalist, and son of the botanist Franz Carl Mertens. Mertens was aboard the Russian vessel Senyavin under Captain Lieutenant Fedor Petrovich Litke with orders to explore the coasts of Russian America and Asia. This turned out to be one of the most productive voyages of disc...
2015 sports event International sporting eventRugby sevens at the 2015 Pan American GamesVenuesExhibition StadiumDatesJuly 11–12, 2015Competitors72 from 6 nationsMedalists Canada United States Brazil2019» Rugby sevens at the2015 Pan American GamesTournamentmenwomenRostersmenwomenvte The women's tournament of rugby sevens at the 2015 Pan American Games was held in Toronto, Ontario, Canada from July 11 to 12.[1] The rugby sevens competi...
Hospital in Greater Accra, GhanaLEKMA HospitalHealth service providerGeographyLocationTeshie, Greater Accra, GhanaLinksWebsitewww.lekmahospital.orgListsHospitals in Ghana The Ledzokuku-Krowor Municipal Assembly (LEKMA) Hospital is a hospital located at Teshie in Accra. It is a government hospital.[1][2][3] References ^ ABOUT US – LEKMA HOSPITAL. Retrieved 2018-12-19. ^ Lekma Hospital. HITA e.V. Retrieved 2018-12-19. ^ LEKMA Hospital brings healthcare closer to the pe...
Princess Louise, at Masset, BC, circa 1880. History NamePrincess Louise OwnerHudson's Bay Company; Canadian Pacific Railway, others. RouteSan Francisco Bay, Puget Sound, coastal British Columbia and southeast Alaska In service1869 Out of service1919 (unpowered after 1906) IdentificationCanadian #72682: US #19297 FateSank at Port Alice, British Columbia General characteristics Tonnage971 gross tons. Length180 ft (55 m) Beam30 ft (9 m) Depth12.5 ft (4 m) depth of h...
Romanian poet (1916–1944) Isanos on a postage stamp of Moldova (2011) Grave at Bellu Cemetery Magda Isanos (17 April 1916 – 17 November 1944) was a Romanian poet. Biography Born in Iași, her parents were Mihail Isanos and his wife Elisabeta (née Bălan), doctors at the Costiugeni psychiatric hospital near Chișinău.[1] Elisabeta was the sister of Elena Alistar.[2] After graduating from the Diocesan High School in Chișinău, Magda entered the law faculty of the Universi...
Merkið Vexillologisches Symbol: [1] Seitenverhältnis: 8:11 Offiziell angenommen: 5. Juni 1959[1] Die Flagge der Färöer, auf Färöisch Merkið (zu deutsch: Zeichen oder Banner), wurde in ihrer heutigen Form am 5. Juli 1959 offiziell eingeführt. Bei einer Umfrage 2004 gaben 17 % der färöischen Haushalte an, sowohl die Flagge als auch eine Fahnenstange zu besitzen, damit sie öffentlich flaggen können.[2] Inhaltsverzeichnis 1 Beschreibung und Bedeutung 2 Ges...
A map of the Flemish Diamond within Belgium. The municipalities seen as being part of the Flemish Diamond. The Flemish Diamond has a high degree of urban sprawl. The Flemish Diamond (Dutch: Vlaamse Ruit) is the Flemish reference to a network of four metropolitan areas in Belgium, three of which are in the central provinces of Flanders, together with the Brussels Capital Region.[1] It consists of four agglomerations which form the four corners of an abstract diamond shape: Brussels, Gh...
American baseball player (1868–1923) Baseball player Willard Mains1889 baseball card of MainsPitcherBorn: (1868-07-07)July 7, 1868North Windham, MaineDied: May 23, 1923(1923-05-23) (aged 54)Bridgton, MaineBatted: LeftThrew: RightMLB debutAugust 3, 1888, for the Chicago White StockingsLast MLB appearanceJune 2, 1896, for the Boston BeaneatersMLB statisticsWin–loss record16–17Earned run average3.53Strikeouts96 Teams Chicago White Stockings (1888) Cincinn...
Nissan Ireland Ltd.TypeSubsidiaryIndustryAutomotiveFounded2 February 1977; 46 years ago (1977-02-02)HeadquartersDublin, IrelandProductsAutomobilesParentNissanWebsitewww.nissan.ie Nissan Ireland Ltd. is the Irish subsidiary of Nissan Motor Company of Japan. With an assembly plant for motor vehicles, it was part of the automotive industry in Ireland.[1] Company history The company was founded in Dublin on 2 February 1977.[2] This was preceded by a relationship ...
Mountain range in Asia This article is about a mountain range. For other uses, see Himalaya (disambiguation). The HimalayasThe arc of the Himalayas (also Hindu Kush and Karakorams) showing the eight-thousanders (in red); Indo-Gangetic Plain; Tibetan plateau; rivers Indus, Ganges, and Yarlung Tsangpo-Brahmaputra; and the two anchors of the range (in yellow)Highest pointPeakMount Everest, Nepal and ChinaElevation8,848.86 m (29,031.7 ft)Coordinates27°5...