Hogyan befolyásolták a szolgáltatások a fejlesztői függőség-injektálási viselkedést - Flagbit Blog

Legalábbis az egységtesztek folyamatosan növekvő népszerűsége óta, különösen a PHPUnit révén, az osztályok függőségeinek megoldása a PHP függőségi injekciójával egyre fontosabb szerepet játszik a fejlesztők mindennapjaiban. Azok az osztályok, amelyek egy vagy több függőséggel rendelkeznek más osztálytípusoktól, akkor kerülnek átadásra, amikor létrehozásuk a konstruktoron keresztül, vagy futás közben a metódushívásokon keresztül történik. Ez javítja az egyes osztályok fenntarthatóságát, és a függőségek könnyebben cserélhetők, ha a követelmények megváltoznak. Javul az egyes komponensek tesztelhetősége is, amelyben az osztályfüggőségeket egyszerű tesztduplákkal lehet helyettesíteni. Hátrány azonban a megnövekedett kazánlapkód, amikor mindig ugyanazokat az objektumokat kell létrehozni a projekt különböző pontjain, hogy fedezzék ugyanazokat a függőségeket.
Az úgynevezett service locator került bevezetésre a jelenlegi PHP keretrendszerekkel. A szolgáltatás lokátorának feladata a szolgáltatás függőségeinek belső megoldása és példányosítása. Ez azt jelenti, hogy objektumpéldányok létrehozhatók egy szolgáltatás segítségével anélkül, hogy hozzá kellene adni a kazánlap kódot az üzleti logika függőségeinek feloldásához. Ezenkívül a szolgáltatás lokátor biztosítja a hívott szolgáltatás egyetlen példányát egy kérésen belül, hogy elkerülje a felesleges objektum létrehozását a klasszikus szingulett hátrányai nélkül.
A Service Locator könnyű kezelhetősége azonban olyan irányokba terelte a függőségi injekciót, amelyek technikai szempontból gyorsan megvalósíthatók, de amelyek nem teljesen követik a célját. A függőségek feloldása az osztályok színes összevonásává vált.
Az alábbiakban szeretném röviden bemutatni azokat az eseteket, amelyekkel hébe-hóba találkoztam. Ezek általában első ránézésre nem tűnnek rossznak, de hátrányaik lehetnek a további fejlődés során.
Szerviz lokátor befecskendezése
Valószínűleg a legismertebb rossz szokás a Szolgáltatás lokátor hozzáadása osztályának függőségeként. Általában a „Talán még szükségem van valamelyik szolgáltatásra” érvvel ez a minta inkább annak a jele, hogy az ember nem tervezett eleget a fejlesztésében. Soha egyetlen osztálynak sem kell minden szolgáltatás. Néha olyan megjegyzéseket is találhat a Google-on, amelyek úgy gondolják, hogy rendben van, ha egy gyár megkapja a szolgáltatáskeresőt. Még egy gyárnak is kevés szolgáltatásra van szüksége a működéséhez. Ha több mint 4 egyéb szolgáltatás függ, újra kell gondolkodnia a gyár feladatán és felépítésén.
A bonyolultság különösen nyilvánvaló az egységtesztek megírásakor. Az osztály általában használható a függőségek azonosítására a konstruktor metódusfejéből vagy a szetter módszerekből. A szolgáltatás lokátorának hozzáadásakor nem csak hozzá kell létrehozni egy tesztdupla-t, hanem az összes tényleges függőséget, amelyet lekérnek belőle. Azt is nehéz megmondani a kódból, hogy mi a szolgáltatás tényleges típusa. Mivel a PHP egy osztálya tartalmazhat olyan módszereket is, amelyek nem interfészről származnak, az azonos interfésszel rendelkező osztályok felcserélhetősége már nem garantált.
Ideális esetben a függőségeket át kell adni, és nem egy másik objektumból kell átvenni.
Ha például megnézi a Symfony 2-et, akkor látni fogja, hogy a Service Locator-t is itt használják közvetlenül. A legismertebb példák a vezérlők és a parancsok, de csak akkor, ha ezek a ContainerAware felületet valósítják meg, ami szokásos. De vannak lehetőségek a vezérlők és a parancsok számára is, hogy ezt elkerüljék.
Fakitermelés
Az információk naplózása fontosabb lehet egy projekt számára, mint egy másik, és eltérhet az információ mennyiségében. Ezért van az a megközelítés, hogy a naplózó példányt véglegesen beillesztik függőségként egy osztály szolgáltatásába. Nagyon kevés esetben azonban a naplózó valóban függőség, mert először fel kell tennie egy kérdést magának: "A szolgáltatásom továbbra is működik naplózó nélkül?"
A tervezési döntéstől függően elegendő lehet kivételt dobni az osztályon belül, és a hibakezelés mellett naplóbejegyzést is hozzáadni egy elkapási blokkba.
Egy másik lehetőség az üzenetek összegyűjtése annak érdekében, hogy később kimenjenek, amint az például a validátoroktól ismert. Itt is létrehozhatók a naplóbejegyzések az osztályon kívül, és így a naplózó függőségként kivonható.
Bizony, ez nem egy durva függőségi megsértés, de ha könnyű a naplózót távol tartani egy osztálytól, akkor a fejlesztőnek kell. Az automatizált tesztek felesleges összetevője, és nincs jelentősége a vizsgált osztály funkciójának.
Túl sok függőség
Aki az osztály konstruktorának sok függőségét nézi és kényelmetlenül érzi magát, valószínűleg felismerte a problémát. A szolgáltatáskonfigurációk egyszerű kezelése megkönnyíti az új függőségek hozzáadását, mivel a fejlesztőnek általában nem kell aggódnia az ebből eredő változások miatt a példányok létrehozásakor. A függőségek száma növekedhet a projekt előrehaladtával, ami olyan bonyolultságot eredményez, amely vonakodik érinteni és változtatni.
Számos függőség-befecskendező tároló lehetőséget kínál a szolgáltatásfüggőségek beállítására a konfiguráció szetteres módszereivel. Ennek eredményeként néhány fejlesztő általában a konstruktor helyett szetter módszerekkel állítja be a függőségeket, de ez nem oldja meg a problémát. Ellenkezőleg: A beállítókat kifejezetten és csak akkor kell beállítani, ha az objektum már létezik. A szolgáltatáskonfiguráció elfogadja a beállítók használatát, de ez az osztály csak ezen beállítók ismeretében használható, például egységtesztekhez, és ha nincsenek megadva kötelező függőségek, akkor a fejlesztőnek gondoskodnia kell a hibakezelésről is . Ez feleslegesen növeli az érintett osztályt. Ezekre nincs szükség a konstruktoron keresztüli függőségi injektáláshoz. Tehát hogyan kell kezelni ezt a problémát?
Jó jel, hogy az osztály többet csinál, mint kellene.
Ebben az esetben fel kell osztani a feladataikat, amelyek ezután az eredeti osztály tényleges függőségeit képviselik. Nem zárható ki, hogy az eredeti osztályra már nincs szükség, és ezért nem alkalmazható, mivel egyetlen feladata a függőségi feladatok végrehajtása.
A függőségek feloldása
Nem minden függőséget kell megoldani a szolgáltatáskonfiguráción keresztül, egyáltalán nem beszélve egy osztály közvetlen függőségéről. Mindazonáltal ezeket a kivitelezőn keresztül, szerviz útján helyezzük be. A szolgáltatások gyors és egyszerű konfigurálása azt jelenti, hogy a változtatások gyorsan elvégezhetők anélkül, hogy meg kellene változtatni azokat a helyeket a kódban, amelyek hívják a szolgáltatást. Ez felkéri a fejlesztőt, hogy oldjon meg mindent az érintett szolgáltatás szolgáltatáskonfigurációjával kapcsolatban.
A következő példában a Foo osztály függ a $ myService-től, amelyet a konstruktor ad át.
A példában azonban ezt a függőséget csak a doSomething módszer használja. Ha a függőségre nem magától az osztálytól, hanem csak az egyik módszerétől van szükség, akkor fontolóra kell venni, hogy a következő megközelítés lehet-e jobb megoldás:
Hátrány: A Foo-t és annak doSomething módszerét használó osztályok a $ myService függvényévé váltak.
Második megközelítésként azt is figyelembe kell venni, hogy a doSomething metódus feladata nem tartozik-e a saját osztályába, és külön szolgáltatásként kell-e beállítani. Ennek az új szolgáltatásnak azonban nem feltétlenül kell a Foo osztály függőségének lennie.
Ebben a példakódban a doSomething metódus feladata teljesen eltávolításra került a Foo osztályból, és egy új „Qux” osztályban került bevezetésre, amely szolgáltatásként az előző példában a $ myService függvénye.
Természetesen ez a példa egyszerű, mert a refaktorálás során más akadályok is akadnak. Az osztályszerkezet megváltoztatásakor a változtatásokat el kell végezni minden olyan helyen, ahol a Foo-t használták. Az osztálystruktúra megváltoztatása még nehezebb, ha azt egy interfész határozza meg.
Függőségi injekció és szolgáltatások
A függőség-injektálás egy olyan esernyő, amelynek sokféle megvalósítási formája van. Annak eldöntését, hogy az űrlapokat melyik esetben kell használni egy adott esetben, nem megadhatja függőségi befecskendező tartály vagy szolgáltató lokátor, hanem magának a fejlesztőnek kell eldöntenie. A keretrendszer olyan funkciókat kínál számunkra, amelyek segítenek bennünket a fejlesztésben, és sok unalmas lépést megkönnyítenek. Hogyan használjuk, még mindig rajtunk múlik.