DI Container
Egy másik fórumtéma kapcsán merült fel bennem a kérdés, hogy konkrétan mit is jelent a „Dependency Injection Container”, mire jó és hogyan kell használni?
Egy minta, ahol gyártófüggvényként lett bemutatva: http://fabien.potencier.org/article/12/do-you-need-a-dependency-injection-container
És egy másik minta, ahol egy asszociatív tömbként: http://pimple.sensiolabs.org
Azért ez a kettő nem egészen ugyanaz, magam részéről a DI-t előszeretettel használom és amikor azt hiszem, hogy DIC-et használok, akkor meg az elsőhöz hasonló szerkezetet, sok „boilerplate kóddal”. :)
Igaz, azt sosem értettem, hogy ezt miért konténernek hívjuk, de valahogy elfogadtam.
Tehát a kérdés: ez csak nekem ellentmondás és tényleg ugyanarról van szó? Vagy az első verzió esetén még nem beszélhetünk DIC-ről, akkor mi az? :)
■ Egy minta, ahol gyártófüggvényként lett bemutatva: http://fabien.potencier.org/article/12/do-you-need-a-dependency-injection-container
És egy másik minta, ahol egy asszociatív tömbként: http://pimple.sensiolabs.org
Azért ez a kettő nem egészen ugyanaz, magam részéről a DI-t előszeretettel használom és amikor azt hiszem, hogy DIC-et használok, akkor meg az elsőhöz hasonló szerkezetet, sok „boilerplate kóddal”. :)
Igaz, azt sosem értettem, hogy ezt miért konténernek hívjuk, de valahogy elfogadtam.
Tehát a kérdés: ez csak nekem ellentmondás és tényleg ugyanarról van szó? Vagy az első verzió esetén még nem beszélhetünk DIC-ről, akkor mi az? :)
Megelőztél :)
Ami engem nagyon zavar e témában, hogy nem csak itt nem tud megegyezni a társaság, hogy mi is ez, hogyan kell használni stb., hanem úgy általában, netszerte eléggé ellentmondásos, zavaros amit találni lehet róla.
Eddig a legérthetőbbnek tűnő leírás ez volt: http://szjani.hu/blog-entry/read/dependency-injection-es-dic#.Tvw9DAw6hr4.twitter
De van egy olyan kellemetlen érzésem, hogy abban a formában, ahogy nekem javasolták, nem igazán célravezető. Inkább valamiféle keretrendszerben tűnik működőképesnek. Pl. a zend és a symfony tartalmaz olyan alkatrészt (DIC - csak ők nem biztos, hogy így hívják), ami kfejezetten e célra lett kialakítva. (ha jól értettem)
Had reagálja a te postodra,
Mindkét általad említett rendszer legújabb verziója tartalmaz DIC-et. Hogy miért jó?
Véleményem szerint nem csak keretrendszerekben van ennek haszna. Ha már van egy adatbázis kapcsolatod, már akkor elgondolkodtató a használata. Nyilván egy kis CLI scriptnél ágyúval verébre lövés.
Ami tényleg fontos: nem minden objektum való DIC-be (hint: service vs. entity típusú objektumok).
Hogy miért konténer? Mert objektumokat tárol és tudja, hogy köztük milyen függőségi viszonyok vannak.
Amit én használok
Ezzel a megoldással az a baj,
A legjobb, ha az ember úgy kezd megírni egy osztályt, hogy definiálja a függőségeket és nem foglalkozik azzal, hogy honnan jönnek majd azok.
Egyetértek
Ha nagyon erőltetni akarnám a megoldást (amit nem akarok, csak gondolkodom), gyakorlatilag fel kellene ruházni az ENV objektumot, hogy a hívó ismeretében döntsön, mit szolgál ki, és mit nem, és kb megkapjuk az eredeti DIC funkcionalítását. Kíváncsian várom a véleményedet.
A DIC lényegét szerintem
Szerintem akkor kell használni, és ebből jön a lényege is, hogy miért jó, ha egy service-t készítesz (pl.: email küldés), akkor az azt reprezentáló objektumot a DIC-ből kéred ki, ami elintézi neked az inicializálást (vagy új objektumot készít, vagy egy már meglévőt használ, mint singleton, singleton használata ugyebár ronda megoldás lenne :)) és nem kell a kapott objektomról többet tudnod mint az interface-e.
Az előzőből következik, hogy így bármikor lecserélheted a service-t egy másik kóddal, csak az interface-nek kell egyeznie.
DIC vs. Service Locator
ZF2 féle
Köszönöm!
Azért most tényleg sajnálom, hogy nincs itt „tesztik”, „köszönöm”, „+1” gomb vagy valami hasonló!
Ez most nem témalezáró hozzászólás szeretne lenni, csak mivel már annyi (számomra) hasznos infó jött, hogy gondoltam ennek már itt a helye!
Amit még mindig nem értek
Vagy csak egy eszköz, amit akár alkalmazáson belül, több más mintával együtt is használhatok?
Hibának számít-e, ha egyiket sem használom, csak megyek a saját fejem után?
Egyáltalán: mennyire tekinthetők ezek az elvek nyelvspecifikusnak?
Gondolok itt olyasmire, hogy sok helyen kifejezetten java v. c# kötődéssel írnak némely tervezési mintákról. Pl. Python-ban, Ruby-ban mennyire érdemes követni ezeket? (PHP-ben vélek felfedezni némi java kötődést, de pl. a python...)
Tud valaki _egyetlen_ olyan címet mondani, ahonnan elindulhatok a tanulással? Már nincs keretem szakkönyvtár beszerzésére, marad a neten ingyenesen elérhető forrás.
Az eddig megtalált, átnézett anyagokkal az a gondom, hogy specifikus témákról szólnak vagy "alapok" címszóval pár szóban elintézik az egész témát.
Minták alatt sokszor objektum
Vannak magasabb szintű minták, amik az alkalmazások, vagy azok egy részének a felépítését, működését definiálják. Vannak EAA, deployment minták. Igazából minden probléma, ami eddig felmerült a szoftverfejlesztésben, azokat valahogy megoldották és kialakultak bevett módszerek.
Mint látható, ezek nagy része nem nyelv specifikus. Az OO design patternek is csak az OOP-re építenek, a legtöbb ilyen minta nyelv független. Többféle problémára többféle pattern létezik, hogy mikor melyiket használod, az rajtad múlik.
No meg vannak még a design principles, ezeket is érdemes tanulmányozni.
Köszi szépen
A Pimple nyomokban sem
A DI lényege az, hogy az osztályod az összes olyan objektumot, amivel együttműködik, kívülről kapja meg, és nem maga példányosítja. Ez az objektumoriántáltság alapelveinek (SOLID) egyikéből következik: minden osztály valamilyen absztrakciónak (interfésznek vagy absztrakt ősosztálynak) egy konkrét implementációja, és egy osztálynak csak a többi osztály által képviselt absztrakcióktól szabad függeni, azok konkrét implementációitól nem. Ez általában úgy van biztosítva, hogy van egy interfész/absztrakt ős, és csak annak a metódusait hívod meg, így a mögöttes implementáció bármikor kicserélhető; nyilván ha az osztályod példányosít egy másik osztályt, akkor ez az elv borul, mert ott lesz az osztályod kódjában a másik osztály neve, vagyis hozzá leszel kötve egy konkrét implementációhoz.
A DI során tehát az osztályod minden olyan objektumot, aminek a függvényeit hívni fogja, kívülről kapja meg, és azok fájdalommentesen kicserélhetőek (ez tipikusan unit teszteknél jön elő, amikor mockolni akarod az éppen nem tesztelt osztályokat). Ez egy összetett rendszerben már elég fájdalmas dolog, ha mondjuk kiderül, hogy egy osztálynak szüksége van valamire, amire eddig nem volt, és öt szint távolságban van a példányosítás helyétől, és az összes szinten át kell adogatni az új osztályt. Ennek az elkerülésére szolgál a DIC, amikor is az összes objektumodat bepakolod egy konténerbe (a gyakorlatban a konténer általában maga végzi a példányosítást, de ez csak implementációs részlet), és az osztályod nem azokat az objektumokat kapja meg, amikre szüksége van, hanem a konténert, és attól lekérheti őket. Ennek vannak hátrányai (egyrészt így eggyel több osztálytól fog függeni, másrészt sokkal nehezebben áttekinthetővé válik a függések rendszere, illetve bárhol hozzáférsz bármihez, míg a sima DI rákényszerít a logikusan elszeparált felépítésre), viszont sokkal egyszerűbbé válik a kód átszervezése.
Úgy is meg lehet fogalmazni, hogy a programodnak van egy kollaborációs gráfja (melyik osztályok melyik más osztályokból hívnak meg függvényeket) meg egy példányosítási gráfja (melyik osztály milyen objektumokat példányosít). Egy naiv programban ez a kettő eléggé hasonló egymáshoz (az osztályok általában maguk példányosítják azokat az objektumokat, amikre szükségük van), és ez ellentmond a single responsibility elvnek: az osztályod a tényleges feladata mellett egy másikat (példányosítás) is végezni kényszerül, és ahogy a példányosítás nemtriviálissá válik (változó konfiguráció, éles és dev környezetek, mockok), ez egyre inkább kezelhetetlenné lesz. A DIC kiszervezi a példányosítás/konfigurációkezelés/környezetkezelés feladatát egy külön osztályba (a sima DI is egyébként, csak ott ez valamelyik legfelső osztály lesz, egy front controller vagy ilyesmi).
Amit leírtál...
Valamint az is, hogy példányosítani tképp csak konténerben szabadna, mert osztályon belül példányosítva sértem a SRP-t.
(konténer nélkül mikor, hol, hogyan lehetne példányosítani?)
DI nélkül
Lehet
Rosszul értelmezem?
DIC
Nyilván egy projekt sikere
A DIC egy-egy meghívása elé
/** @var SomeClass */
, pont ugyanannyiba kerül, mintha lenne a konstruktornak egy extra paramétere, és ahhoz írnál PHPdocot, úgyhogy ez a kódkiegészítéses dolog nekem nem hangzik meggyőzően. A string paraméterezést meg semmibe se kerül átalakítani __call-á, ha az jobban tetszik, bár nem értem, miért befolyásolná a debuggolhatóságot. Az tény, hogy szorosabban csatolt lesz tőle a kód, mert minden osztályod függ a konténertől - a gyakorlatban azért egy DIC elég egyszerű ahhoz, hogy sose kelljen visszafelé nem kompatibilis változtatásokat végezni rajta (de ha mégis, az nagy szívás). DIC nélkül viszont az a nagy szívás, ha át kell szervezni a programot, és jelentősen megváltoznak a függőségek, és minden X idejű átíráshoz 3X ideig kell javítgatni a függőségek közvetítését.C-ben is írtak jól működő nagy projekteket... az, hogy milyen tervezési mintákat használsz, sokadlagos tényező a sikerességben ahhoz képest, hogy mennyire átgondolt és jól szervezett a kód, mennyire lettek jól felmérve a követelmények stb. Nem akarok úgy csinálni, mintha hű de nagy tapasztalatom lenne a témában; mindenesetre én az utolsó projektemnél nem használtam DIC-t (én is a túl központosított függőségek miatt idegenkedtem tőle), és sokat szívtam miatta. (Ami persze nem zárja ki, hogy ha használok, akkor más miatt szívtam volna, de ha újra kéne kezdenem, DIC-vel csinálnám.)
Kódkiegészítés
Szóval csináltam egy ilyet:
Vagy szokás szerint félreértek valamit?
Azt hiszem, a
Szóval félreértettem
(és hát igen, amit janoszen lentebb írt, abban is van valami)
Azt kéne használniuk, de nem
Nem tudom, miért döntött a NetBeans (ill. az Eclipse, amiből ők átvették) éppen e mellett a szintaxis mellett.
Pont ez a baj
Hát ha a visszaadandó
Fejben
Az IDE nem írja meg helyetted
Nem akarok úgy csinálni,
Nekem van olyan ismerősöm, akinek hú de nagy tapasztalata van a témában, multi cégnél dolgozik, azt hiszem középvezető beosztásban (felügyel 20+ emberre). Megkérdeztem, azt mondta neki sem indul egy projektnek unit test-ek és DIC nélkül. Persze ez is olyan dolog, mint hogy valaki inkább notepad-ben szerkeszt, mert nem kell neki IDE. Embere válogatja... :-)
Ezen felül engem zavar, hogy
Alig pár objektumnak szabad csak lennie, ami közvetlenül a DIC-et használja. Mondjuk egy ZF-es alkalmazásnál a bootstrap folyamán a DIC-ből szedem ki a pluginokat és regisztrálom be őket (technológiai problémák miatt, normális keretrendszer esetén erre nem lenne szükség), valamint a controllerekben használom. Ennyi, sehol máshol.
Objektumorientált programot
Konténer nélkül tipikusan a rendszer indításakor indítasz, a legelsőként lefutó osztályban, ami beolvassa a konfigurációs fájlokat, és létrehozza a tényleges vezérlő objektumot. MVC-s, routingos webalkalmazásnál ez a front controller, vagyis az a fájl, ami elkapja az összes requestet.
P.E.B :-)
Hát igen, pedig ilyesmire gondoltam, ha nem is ezen a néven. :-)
Inkább úgy fogalmaznék, hogy ha vannak olyan elmebeteg álmaim, hogy majdan ebből éljek és netán valahol foglalkoztatnának egy kezdő, de OOP-t használni tudó egyént, akkor foglalkoznak-e az illető jelentkezésével amennyiben nem követi pontról pontra a SOLID elveit?
Hát, szerintem az esetek nagy
A DI lényege az, hogy az
Na ez a lényeg :-) Olyan szépen megfogalmaztad, hogy lehet, hogy idézni foglak :D
A DIC-eknél szerintem ugyanúgy meg kell jelennie egy példányosítási gráfnak, ha mindent egyetlen DIC-ben tárolsz az god object anti pattern.
A DIC másra is jó. :-)Egy
Egy átlagos osztály viselkedése körülbelül így néz ki:
Na most én állandóan abba a problémába ütközöm, hogy elfelejtem lefuttatni a konfigurálást. Ezért általában valamelyik "egyezményes" setter-be teszem, hogy hívja meg automatikusan. A DIC-vel ezt ki lehet váltani, mert annak direkt az a feladata, hogy az első 3 pontot teljesítse újrafelhasználható módon...