Függőségek átadása
Az argumentumok, vagy DI terminológiában „függőségek“, a következő főbb módokon adhatók át az osztályoknak:
- konstruktor általi átadás
- átadás metóduson keresztül (úgynevezett setter)
- egy tulajdonság beállításával
- módszerrel, annotációval vagy attribútummal injektálással.
Most konkrét példákkal illusztráljuk a különböző változatokat.
Konstruktor-befecskendezés
A függőségek az objektum létrehozásakor argumentumként kerülnek átadásra a konstruktornak:
Ez a forma olyan kötelező függőségek esetében hasznos, amelyekre az osztálynak feltétlenül szüksége van a működéshez, mivel nélkülük a példány nem hozható létre.
A PHP 8.0 óta használhatunk egy rövidebb jelölési formát, amely funkcionálisan egyenértékű (constructor property promotion):
A PHP 8.1 óta egy tulajdonságot megjelölhetünk egy readonly
jelzővel, amely kijelenti, hogy a tulajdonság
tartalma nem fog változni:
A DI konténer automatikusan átadja a függőségeket a konstruktornak az autowiring segítségével. Az ilyen módon nem átadható argumentumok (pl. stringek, számok, booleans) a konfigurátorban íródnak.
Constructor Hell
A construktor hell kifejezés arra a helyzetre utal, amikor egy gyermek egy olyan szülő osztálytól örököl, amelynek konstruktora függőségeket igényel, és a gyermek is függőségeket igényel. A szülő függőségeit is át kell vennie és tovább kell adnia:
A probléma akkor jelentkezik, amikor a BaseClass
osztály konstruktorát meg akarjuk változtatni, például egy
új függőség hozzáadásakor. Ekkor a gyerekek összes konstruktorát is módosítani kell. Ami pokollá teszi az ilyen
módosítást.
Hogyan lehet ezt megelőzni? A megoldás az, hogy elsőbbséget adunk a kompozíciónak az örökléssel szemben.
Tervezzük meg tehát a kódot másképp. Kerüljük az absztrakt
Base*
osztályokat. Ahelyett, hogy a MyClass
a BaseClass
osztályból örökölve kapna
bizonyos funkciókat, ahelyett, hogy függőségként átadnánk ezeket a funkciókat:
Setter injektálás
A függőségek átadása egy olyan metódus meghívásával történik, amely egy privát tulajdonságban tárolja őket.
Ezeknek a metódusoknak a szokásos elnevezési konvenciója a set*()
, ezért hívják őket settereknek, de
természetesen hívhatók máshogy is.
Ez a metódus olyan opcionális függőségek esetében hasznos, amelyek nem szükségesek az osztály működéséhez, mivel nem garantált, hogy az objektum valóban megkapja őket (azaz a felhasználó meghívja a metódust).
Ugyanakkor ez a metódus lehetővé teszi a setter ismételt meghívását a függőség megváltoztatására. Ha ez nem
kívánatos, adjunk hozzá egy ellenőrzést a metódushoz, vagy a PHP 8.1-től kezdve jelöljük meg a $cache
tulajdonságot a readonly
flaggel.
A setter hívás a DI konténer konfigurációjában a setup szakaszban van definiálva. Itt is a függőségek automatikus átadását használja az autowiring:
Property Injection
A függőségek közvetlenül a tulajdonsághoz kerülnek átadásra:
Ez a módszer nem tekinthető megfelelőnek, mivel a tulajdonságot a public
címen kell deklarálni. Így nincs
befolyásunk arra, hogy az átadott függőség valóban a megadott típusú lesz-e (ez a PHP 7.4 előtt volt igaz), és
elveszítjük a lehetőséget, hogy saját kódunkkal reagáljunk az újonnan hozzárendelt függőségre, például a későbbi
változások megakadályozására. Ugyanakkor a tulajdonság az osztály nyilvános interfészének részévé válik, ami nem
feltétlenül kívánatos.
A változó beállítását a DI konténer konfigurációjában, a setup szakaszban határozzuk meg:
Injektálás
Míg az előző három módszer általában minden objektumorientált nyelvben érvényes, a metódus, annotáció vagy inject attribútum általi injektálás a Nette prezenterekre jellemző. Ezeket külön fejezetben tárgyaljuk.
Melyik utat válasszuk?
- A konstruktor alkalmas a kötelező függőségekre, amelyekre az osztálynak szüksége van a működéshez.
- a setter ezzel szemben az opcionális, vagy megváltoztatható függőségekhez alkalmas.
- a nyilvános változók nem ajánlottak