Gyakran Ismételt Kérdések a DI-ről (GYIK)
- A DI egy másik név az IoC-re?
- Mi az a Service Locator?
- Mikor jobb nem használni a DI-t?
- Vannak árnyoldalai a DI használatának?
- Hogyan írjunk át egy legacy alkalmazást DI-re?
- Miért részesítjük előnyben a kompozíciót az öröklődéssel szemben?
- Használható a Nette DI Container a Nette-n kívül?
- Miért van a konfiguráció NEON fájlokban?
- Nem lassítja le az alkalmazást a NEON fájlok feldolgozása?
- Hogyan férek hozzá az osztályomból a konfigurációs fájl paramétereihez?
- Támogatja a Nette a PSR-11: Container interface-t?
A DI egy másik név az IoC-re?
Az Inversion of Control (IoC) egy elv, amely arra összpontosít, hogyan fut a kód – hogy a kódja futtat-e egy
idegen kódot, vagy a kódja integrálva van egy idegen kódba, amely aztán meghívja. Az IoC egy tág fogalom, amely magában
foglalja az eseményeket, az úgynevezett Hollywood-elvet és más szempontokat is. Ennek a
koncepciónak a része a factory is, amelyről a 3. szabály: hagyd a
factory-ra szól, és amely az new
operátor inverzióját jelenti.
A Dependency Injection (DI) arra összpontosít, hogyan tud meg egy objektum egy másik objektumról, azaz annak függőségeiről. Ez egy tervezési minta, amely megköveteli a függőségek explicit átadását az objektumok között.
Tehát mondhatjuk, hogy a DI az IoC egy specifikus formája. Azonban nem minden IoC forma megfelelő a kód tisztasága szempontjából. Például az antipattern-ek közé tartoznak azok a technikák, amelyek globális állapottal dolgoznak, vagy az úgynevezett Service Locator.
Mi az a Service Locator?
Ez egy alternatíva a Dependency Injection-re. Úgy működik, hogy létrehoz egy központi tárolót, ahol minden elérhető szolgáltatás vagy függőség regisztrálva van. Amikor egy objektumnak szüksége van egy függőségre, a Service Locatortól kéri azt.
A Dependency Injection-nel szemben azonban elveszíti az átláthatóságot: a függőségek nem közvetlenül kerülnek átadásra az objektumoknak, és így nem könnyen azonosíthatók, ami megköveteli a kód átvizsgálását, hogy minden kapcsolatot feltárjunk és megértsünk. A tesztelés is bonyolultabb, mert nem tudunk egyszerűen mock objektumokat átadni a tesztelt objektumoknak, hanem a Service Locatoron keresztül kell ezt megtennünk. Ráadásul a Service Locator megzavarja a kód tervezését, mivel az egyes objektumoknak tudniuk kell a létezéséről, ami eltér a Dependency Injection-től, ahol az objektumoknak nincs tudomásuk a DI konténerről.
Mikor jobb nem használni a DI-t?
Nincsenek ismert nehézségek a Dependency Injection tervezési minta használatával kapcsolatban. Ellenkezőleg, a függőségek globálisan elérhető helyekről való beszerzése számos komplikációhoz vezet, ahogy a Service Locator használata is. Ezért célszerű mindig DI-t használni. Ez nem dogmatikus megközelítés, egyszerűen nem találtak jobb alternatívát.
Ennek ellenére léteznek bizonyos helyzetek, amikor nem adunk át objektumokat, és a globális térből szerezzük be őket. Például a kód debuggolásakor, amikor egy adott ponton ki kell íratni egy változó értékét, meg kell mérni egy programrész futási idejét, vagy naplózni kell egy üzenetet. Ilyen esetekben, amikor ideiglenes műveletekről van szó, amelyeket később eltávolítanak a kódból, legitim egy globálisan elérhető dumper, stopperóra vagy logger használata. Ezek az eszközök ugyanis nem tartoznak a kód tervezéséhez.
Vannak árnyoldalai a DI használatának?
Jár-e a Dependency Injection használata valamilyen hátránnyal, például megnövekedett kódírási igénybevétellel vagy rosszabb teljesítménnyel? Mit veszítünk, ha elkezdünk DI-kompatibilis kódot írni?
A DI nincs hatással az alkalmazás teljesítményére vagy memóriaigényére. A DI Container teljesítménye játszhat némi szerepet, azonban a Nette DI esetében a konténer tiszta PHP-ba van fordítva, így a futásidejű overhead lényegében nulla.
A kódírás során szükség lehet konstruktorok létrehozására, amelyek függőségeket fogadnak el. Korábban ez időigényes lehetett, de a modern IDE-knek és a constructor property promotion-nek köszönhetően ez most másodpercek kérdése. A factory-kat könnyen lehet generálni a Nette DI és a PhpStorm plugin segítségével egy egérkattintással. Másrészt nincs szükség singletonok és statikus hozzáférési pontok írására.
Megállapítható, hogy egy helyesen megtervezett, DI-t használó alkalmazás sem rövidebb, sem hosszabb nem lesz egy singletonokat használó alkalmazáshoz képest. A függőségekkel dolgozó kódrészek csupán ki vannak emelve az egyes osztályokból, és új helyekre kerülnek, azaz a DI konténerbe és a factory-kba.
Hogyan írjunk át egy legacy alkalmazást DI-re?
Egy legacy alkalmazás átállítása Dependency Injection-re kihívást jelentő folyamat lehet, különösen nagy és komplex alkalmazások esetén. Fontos, hogy ezt a folyamatot szisztematikusan közelítsük meg.
- A Dependency Injection-re való áttéréskor fontos, hogy a csapat minden tagja megértse az alkalmazott elveket és eljárásokat.
- Először végezzen elemzést a meglévő alkalmazásról, és azonosítsa a kulcsfontosságú komponenseket és azok függőségeit. Készítsen tervet arról, mely részeket kell refaktorálni és milyen sorrendben.
- Implementáljon egy DI konténert, vagy még jobb, ha egy létező könyvtárat használ, például a Nette DI-t.
- Fokozatosan refaktorálja az alkalmazás egyes részeit, hogy Dependency Injection-t használjanak. Ez magában foglalhatja a konstruktorok vagy metódusok módosítását úgy, hogy paraméterként fogadják el a függőségeket.
- Módosítsa azokat a kódrészeket, ahol függőségekkel rendelkező objektumok jönnek létre, hogy ehelyett a függőségeket a konténer injektálja. Ez magában foglalhatja a factory-k használatát.
Ne feledje, hogy a Dependency Injection-re való áttérés befektetés a kód minőségébe és az alkalmazás hosszú távú fenntarthatóságába. Bár kihívást jelenthet ezeknek a változtatásoknak a végrehajtása, az eredmény egy tisztább, modulárisabb és könnyen tesztelhető kód kell, hogy legyen, amely készen áll a jövőbeli bővítésre és karbantartásra.
Miért részesítjük előnyben a kompozíciót az öröklődéssel szemben?
Célszerűbb a kompozíciót használni az öröklődés helyett, mert a kód újrafelhasználására szolgál anélkül, hogy aggódnunk kellene a változtatások következményei miatt. Tehát lazább kötést biztosít, ahol nem kell attól tartanunk, hogy egy kód módosítása szükségessé teszi egy másik függő kód módosítását. Tipikus példa erre a constructor hell néven ismert helyzet.
Használható a Nette DI Container a Nette-n kívül?
Határozottan. A Nette DI Container a Nette része, de önálló könyvtárként lett tervezve, amely a keretrendszer többi részétől függetlenül használható. Csak telepíteni kell a Composer segítségével, létre kell hozni egy konfigurációs fájlt a szolgáltatások definíciójával, majd néhány sor PHP kóddal létre kell hozni a DI konténert. És azonnal elkezdheti kihasználni a Dependency Injection előnyeit a projektjeiben.
A konkrét használatot, beleértve a kódokat is, a Nette DI Container fejezet írja le.
Miért van a konfiguráció NEON fájlokban?
A NEON egy egyszerű és könnyen olvasható konfigurációs nyelv, amelyet a Nette keretében fejlesztettek ki alkalmazások, szolgáltatások és azok függőségeinek beállítására. A JSON-nal vagy YAML-lel összehasonlítva sokkal intuitívabb és rugalmasabb lehetőségeket kínál erre a célra. A NEON-ban természetesen leírhatók olyan kapcsolatok, amelyeket a Symfony & YAML-ben vagy egyáltalán nem lehetne leírni, vagy csak bonyolult leírással.
Nem lassítja le az alkalmazást a NEON fájlok feldolgozása?
Bár a NEON fájlok nagyon gyorsan feldolgozódnak, ez a szempont egyáltalán nem számít. Az ok az, hogy a fájlok feldolgozása csak egyszer történik meg az alkalmazás első indításakor. Ezután legenerálódik a DI konténer kódja, elmentődik a lemezre, és minden további kérésnél elindul anélkül, hogy további feldolgozásra lenne szükség.
Ez így működik a produkciós környezetben. A fejlesztés során a NEON fájlok minden alkalommal feldolgozódnak, amikor a tartalmuk megváltozik, hogy a fejlesztő mindig naprakész DI konténerrel rendelkezzen. Maga a feldolgozás, ahogy említettük, pillanatok kérdése.
Hogyan férek hozzá az osztályomból a konfigurációs fájl paramétereihez?
Tartsuk szem előtt az 1. szabályt: kérd el, hogy átadják. Ha egy osztálynak információra van szüksége a konfigurációs fájlból, nem kell azon gondolkodnunk, hogyan jussunk hozzá ehhez az információhoz, ehelyett egyszerűen kérjük el – például az osztály konstruktorán keresztül. Az átadást pedig a konfigurációs fájlban valósítjuk meg.
Ebben a példában a %myParameter%
a myParameter
paraméter értékének helyettesítője, amelyet
átadunk a MyClass
osztály konstruktorának:
# config.neon
parameters:
myParameter: Some value
services:
- MyClass(%myParameter%)
Ha több paramétert szeretne átadni, vagy autowiringot szeretne használni, célszerű a paramétereket objektumba csomagolni.
Támogatja a Nette a PSR-11: Container interface-t?
A Nette DI Container nem támogatja közvetlenül a PSR-11-et. Azonban, ha interoperabilitásra van szüksége a Nette DI Container és olyan könyvtárak vagy keretrendszerek között, amelyek PSR-11 Container Interface-t várnak, létrehozhat egy egyszerű adaptert, amely hídként szolgál a Nette DI Container és a PSR-11 között.