Ugrás a tartalomhoz

A függőség befecskendezése

Ellenőrzött
A Wikipédiából, a szabad enciklopédiából

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.

John Munsch, 28 October 2009.[6][7][8]

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]
Példa UML osztály- és folyamatdiagram for the Dependency Injection design patterna dependency injection mintáról.[28]

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]
  1. I.T., Titanium: James Shore: Dependency Injection Demystified. www.jamesshore.com . (Hozzáférés: 2015. július 18.)
  2. HollywoodPrinciple. http://c2.com . (Hozzáférés: 2015. július 19.)
  3. Inversion of Control Containers and the Dependency Injection pattern. (Hozzáférés: 2015. július 18.)
  4. The Dependency Injection design pattern - Problem, Solution, and Applicability. w3sDesign.com . (Hozzáférés: 2017. augusztus 12.)
  5. 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 
  6. a b Dependency Injection in .NET. Manning Publications, 4. o. (2011. október 1.). ISBN 9781935182504 
  7. 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.)
  8. How to explain dependency injection to a 5-year-old?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
  9. 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
  10. Seemann, Mark: Dependency Injection is Loose Coupling. blog.ploeh.dk . (Hozzáférés: 2015. július 28.)
  11. 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.)
  12. 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.)
  13. 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.)
  14. 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.)
  15. How to write testable code. www.loosecouplings.com . (Hozzáférés: 2015. július 18.)
  16. Writing Clean, Testable Code. www.ethanresnick.com . (Hozzáférés: 2015. július 18.)
  17. 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.)
  18. Inversion of Control vs Dependency Injection. stackoverflow.com . (Hozzáférés: 2015. augusztus 5.)
  19. What is the difference between Strategy pattern and Dependency Injection?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
  20. Dependency Injection != using a DI container. www.loosecouplings.com . (Hozzáférés: 2015. július 18.)
  21. 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.)
  22. 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.)
  23. the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns. www.bryancook.net . (Hozzáférés: 2015. július 18.)
  24. The Dependency Injection Design Pattern. msdn.microsoft.com . (Hozzáférés: 2015. július 18.)
  25. a b What are the downsides to using Dependency Injection?. stackoverflow.com . (Hozzáférés: 2015. július 18.)
  26. a b Dependency Injection Inversion - Clean Coder. sites.google.com . (Hozzáférés: 2015. július 18.)
  27. Decoupling Your Application From Your Dependency Injection Framework. InfoQ . (Hozzáférés: 2015. július 18.)
  28. The Dependency Injection design pattern - Structure and Collaboration. w3sDesign.com . (Hozzáférés: 2017. augusztus 12.)
  29. 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.)
  30. 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.)
  31. AccessibleObject (Java Platform SE 7). docs.oracle.com . (Hozzáférés: 2015. július 18.)
  32. 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>
  33. 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.)
  34. 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.)
  35. Pro Spring Dynamic Modules for OSGi Service Platforms. APress. (Hozzáférés: 2015. július 6.)
  36. Captain Debug's Blog: Is ‘Convention Over Configuration’ Going Too Far?. www.captaindebug.com . (Hozzáférés: 2015. július 18.)
  37. Decker, Colin: What's the issue with @Inject? | Colin's Devlog. blog.cgdecker.com . (Hozzáférés: 2015. július 18.)
  38. 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.)
  39. 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.