A függőség befecskendezése
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
[szerkesztés]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
[szerkesztés]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
[szerkesztés]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.
Keretrendszerek
[szerkesztés]Alkalmazás keretrendszerek, mint CDI és megvalósításai Weld, Spring, Guice, Play framework, Salta, Glassfish HK2, Dagger, és Managed Extensibility Framework (MEF) támogatják, de nem teszik kötelezővé a dependency injectiont.[20][21]
Előnyei
[szerkesztés]- 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
[szerkesztés]- 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
[szerkesztés]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
[szerkesztés]Közvetlen példányosítás
[szerkeszté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 injection
public class Client {
// Internal reference to the service used by this client
private ServiceExample service;
// Constructor
Client() {
// Specify a specific implementation in the constructor instead of using dependency injection
service = new ServiceExample();
}
// Method within this client that uses the services
public String greet() {
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
[szerkesztés]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ó
[szerkesztés]A kliens konstruktora paraméterként veszi át a szolgáltató objektumot:
// Constructor
Client(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
Szetter injekció
[szerkesztés]A kliens szetterben veszi át a szolgáltató objektumot, mint paramétert:
// Setter method
public void setService(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
Interfész injekció
[szerkesztés]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.
public interface ServiceSetter {
public void setService(Service service);
}
// Client class
public 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;
}
}
Konstruktor injekció összehasonlítás
[szerkeszté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.
// Constructor
Client(Service service, Service otherService) {
if (service == null) {
throw new InvalidParameterException("service must not be null");
}
if (otherService == null) {
throw new InvalidParameterException("otherService must not be null");
}
// Save the service references inside this client
this.service = service;
this.otherService = otherService;
}
Szetter injekció összehasonlítás
[szerkeszté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 client
public void setService(Service service) {
if (service == null) {
throw new InvalidParameterException("service must not be null");
}
this.service = service;
}
// Set the other service to be used by this client
public void setOtherService(Service otherService) {
if (otherService == null) {
throw new InvalidParameterException("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 client
public void setService(Service service) {
this.service = service;
}
// Set the other service to be used by this client
public void setOtherService(Service otherService) {
this.otherService = otherService;
}
// Check the service references of this client
private void validateState() {
if (service == null) {
throw new IllegalStateException("service must not be null");
}
if (otherService == null) {
throw new IllegalStateException("otherService must not be null");
}
}
// Method that uses the service references
public void doSomething() {
validateState();
service.doYourThing();
otherService.doYourThing();
}
Interfész injekció összehasonlítás
[szerkeszté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
[szerkesztés]A dependency injection megvalósításának egyik módja az, ha kézzel gyűjtjük össze a szereplőket a mainben:
public class Injector {
public static void main(String[] args) {
// Build the dependencies first
Service service = new ServiceExample();
// Inject the service, constructor style
Client client = new Client(service);
// Use the objects
System.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:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Injector {
public static void main(String[] args) {
// -- Assembling objects -- //
BeanFactory beanfactory = new ClassPathXmlApplicationContext("Beans.xml");
Client client = (Client) beanfactory.getBean("client");
// -- Using objects -- //
System.out.println(client.greet());
}
}
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().
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="service" class="ServiceExample">
</bean>
<bean id="client" class="Client">
<constructor-arg value="service" />
</bean>
</beans>
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]
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Injector {
public static void main(String[] args) {
// Assemble the objects
BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class);
Client client = beanfactory.getBean(Client.class);
// Use the objects
System.out.println(client.greet());
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan
static class MyConfiguration {
@Bean
public Client client(ServiceExample service) {
return new Client(service);
}
}
@Component
public class ServiceExample {
public String getName() {
return "World!";
}
}
Az összegyűjtés összehasonlítása
[szerkesztés]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
[szerkesztés]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.
function SomeClass(greeter) {
this.greeter = greeter;
}
SomeClass.prototype.doSomething = function(name) {
this.greeter.greet(name);
}
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 module
var myModule = 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.
var injector = angular.injector(['myModule', 'ng']);
var greeter = injector.get('greeter');
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:
<div ng-controller="MyController">
<button ng-click="sayHello()">Hello</button>
</div>
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}
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.
Jegyzetek
[szerkesztés]- ↑ I.T., Titanium: James Shore: Dependency Injection Demystified. www.jamesshore.com . (Hozzáférés: 2015. július 18.)
- ↑ HollywoodPrinciple. http://c2.com . (Hozzáférés: 2015. július 19.)
- ↑ Inversion of Control Containers and the Dependency Injection pattern. (Hozzáférés: 2015. július 18.)
- ↑ The Dependency Injection design pattern - Problem, Solution, and Applicability. w3sDesign.com . (Hozzáférés: 2017. augusztus 12.)
- ↑ 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
- ↑ a b Dependency Injection in .NET. Manning Publications, 4. o. (2011. október 1.). ISBN 9781935182504
- ↑ Dependency Injection in NET. http://philkildea.co.uk . [2015. július 21-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. július 18.)
- ↑ How to explain dependency injection to a 5-year-old?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
- ↑ 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
- ↑ Seemann, Mark: Dependency Injection is Loose Coupling. blog.ploeh.dk . (Hozzáférés: 2015. július 28.)
- ↑ Passing Information to a Method or a Constructor (The Java™ Tutorials > Learning the Java Language > Classes and Objects). docs.oracle.com . (Hozzáférés: 2015. július 18.)
- ↑ A curry of Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI) and IoC Container - CodeProject. www.codeproject.com . (Hozzáférés: 2015. augusztus 8.)
- ↑ How to force "program to an interface" without using a java Interface in java 1.6. programmers.stackexchange.com . (Hozzáférés: 2015. július 19.)
- ↑ To "new" or not to "new"…. [2020. május 13-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. július 18.)
- ↑ How to write testable code. www.loosecouplings.com . (Hozzáférés: 2015. július 18.)
- ↑ Writing Clean, Testable Code. www.ethanresnick.com . (Hozzáférés: 2015. július 18.)
- ↑ Sironi, Giorgio: When to inject: the distinction between newables and injectables - Invisible to the eye. www.giorgiosironi.com . (Hozzáférés: 2015. július 18.)
- ↑ Inversion of Control vs Dependency Injection. stackoverflow.com . (Hozzáférés: 2015. augusztus 5.)
- ↑ What is the difference between Strategy pattern and Dependency Injection?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
- ↑ Dependency Injection != using a DI container. www.loosecouplings.com . (Hozzáférés: 2015. július 18.)
- ↑ Black Sheep » DIY-DI » Print. blacksheep.parry.org . [2015. június 27-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. július 18.)
- ↑ a b The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330. jcp.org . (Hozzáférés: 2015. július 18.)
- ↑ the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns. www.bryancook.net . (Hozzáférés: 2015. július 18.)
- ↑ The Dependency Injection Design Pattern. msdn.microsoft.com . (Hozzáférés: 2015. július 18.)
- ↑ a b What are the downsides to using Dependency Injection?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
- ↑ a b Dependency Injection Inversion - Clean Coder. sites.google.com . (Hozzáférés: 2015. július 18.)
- ↑ Decoupling Your Application From Your Dependency Injection Framework. InfoQ . (Hozzáférés: 2015. július 18.)
- ↑ The Dependency Injection design pattern - Structure and Collaboration. w3sDesign.com . (Hozzáférés: 2017. augusztus 12.)
- ↑ Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern - Forms of Dependency Injection. Martinfowler.com, 2004. január 23. (Hozzáférés: 2014. március 22.)
- ↑ Yan - Dependency Injection Types. Yan.codehaus.org. [2013. augusztus 18-i dátummal az eredetiből archiválva]. (Hozzáférés: 2013. december 11.)
- ↑ AccessibleObject (Java Platform SE 7). docs.oracle.com . (Hozzáférés: 2015. július 18.)
- ↑ Riehle, Dirk (2000), Framework Design: A Role Modeling Approach, Swiss Federal Institute of Technology, <http://www.riehle.org/computer-science/research/dissertation/diss-a4.pdf>
- ↑ Spring Tips: A POJO with annotations is not Plain. [2015. július 15-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. július 18.)
- ↑ Annotations in POJO – a boon or a curse? | Techtracer. [2019. április 26-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. július 18.)
- ↑ Pro Spring Dynamic Modules for OSGi Service Platforms. APress. (Hozzáférés: 2015. július 6.)
- ↑ Captain Debug's Blog: Is ‘Convention Over Configuration’ Going Too Far?. www.captaindebug.com . (Hozzáférés: 2015. július 18.)
- ↑ Decker, Colin: What's the issue with @Inject? | Colin's Devlog. blog.cgdecker.com . (Hozzáférés: 2015. július 18.)
- ↑ 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.)
- ↑ The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330. www.jcp.org . (Hozzáférés: 2015. július 18.)
Fordítás
[szerkesztés]- 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.