A függőség befecskendezése

(Dependency injection szócikkből átirányítva)

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.