
2016-11-01
Objektumorientált tervezési minták
Egy átlagos, tiszta, felhőtlen éjszakán az emberi szem 2500 csillagot képes érzékelni az égbolton. A Magentoban a nyitóoldal betöltődése során 3000 objektum keletkezik, míg a katalógus oldal betöltődése során 7000 objektum. Így, a Magento egy-egy oldalbetöltődése esetén több objektum keletkezik, mint ahány csillag van az égen - méghozzá szó szerint. Mégis milyen nagy a Magento? Hogyan tartható karban és fejleszthető tovább ekkora komplexitású kódbázis. A választ a standardok adják, objektumok esetén az objektumorientált tervezési minták.
(Ez a cikk a Magento jelenlegi verziójával a Magento 2-vel foglalkozik.)
Az objektumorientált tervezési minták (angolul Object Oriented Design Patterns) általánosan elfogadott eljárások arra, hogyan kell objektumorientáltan programozni. Az objektumorientált fejlesztés a 60-as évek óta létezik, azonban a tervezési minták csak a 90-es években jelentek meg standardizáltan. A tervezési minták egy-egy osztály típus működését írják le. A Magento objektumorientált, minden funkcióját objektumként képzelték el a megalkotói, ezért a tervezési minták alkalmazása kulcsfontosságú a Magento modulok fejlesztése során.
Egyszer volt egy Singleton
A katalógus oldalnál maradva, a hétezer objektumból egyezer objektum singleton, amelyek összesen 33 ezerszer kerülnek meghívásra az oldal betöltődése során. Így, a Magento fejlesztői 33 ezer példányosodást tudtak egyezer példányra redukálni csupán azáltal, hogy a Magentoban keretrendszer szinten implementálták a singleton tervezési mintát. A Magento szuper-absztrakt keretrendszere a Singletonon túl számtalan objektumorientált tervezési mintát alkalmaz. Mindez hatalmas technológiai lökést add a Magento moduloknak és végső soron kapukat nyit meg az egyedi üzleti igények előtt.
Egy objektum singleton, ha nem létezik belőle másik. Ilyen pl. a Magento fájlrendszerét kezelő osztály. Ezt az osztályt nincs értelme többször példányosítani, hiszen minden esetben ugyanúgy beállítja magának a mappákat és ugyanazokat az értékeket adja vissza.
Trükkös, de a Magentoban minden osztály singletonként példányosodik (hacsak nem teszünk ellene). A Magento objektum menedzsere gondoskodik róla, hogy a már példányosodott osztályok mégegyszer ne kerüljenek meghívásra.
Fontos, hogy az objektum menedzsert nem szabad használni, az objektum menedzser a Magento lelkének része, amely a számtalan magic művelet közül végzi az egyiket: osztályokat példányosít, illetve meglévő objektumokat ad vissza az osztály neve alapján. Egy modul fejlesztő az osztályokat mindig a kívánt osztály konstruktorán keresztül injektálja a megfelelő helyre - ezt hívják DI-nek (dependency injection).

A dependency injection biztosítja, hogy az objektum ne legyen példányosítható azon objektumok nélkül, amelyektől a működése függ.
Így születik új objektum - Factory
Mivel a Magentoban alapesetben minden osztály singleton objektumként példányosodik, ezért a fejlesztők egy újabb tervezési mintát hoztak be a Magento keretrendszer szintjére: a Factory-t. Ahogy a neve is utal rá, a factory osztály nem csinál mást mint példányosít.
A Factory tervezési minta lényege, hogy a factory osztály mindig egy adott osztály (jobb esetben interface) implementációinak példányát adja vissza. Ilyenek például a DTO-k (Data Object). Minden DTO-nak van egy factory-ja, így a terméknek is. Gyakorlatban ez úgy valósul meg, hogy a termék objektum példányosodása esetén nem new Product()-tal példányosítunk, hiszen az megkerülné az objetum menedzsert, de nem is dependency injectionön keresztül adjuk át az objektumot, mert akkor pedig singletonként jönne létre ami számtalan problémát vetne fel a későbbiekben, így a megoldás a factory alkalmazása: dependency injectionben adjuk át a termék factory-ját a ProductFactory-t. A ProductFactory osztály példánya egy singleton, amelynek van egy create metódusa, amely visszaad egy új Product típusú objektumot.

A factory osztályokat sosem kell megírni, a Magento saját maga készíti el őket (developer módban on-the-fly, production módban pedig a dependency compile műveletet követően). Ha bármely létező osztály végére a Factory kulcsszót tesszük, pl. SajatOsztalyNeveFactory, akkor a Magento elkészíti ezt az osztályt, amelynek a példányán ha meghívjuk a create metódust a SajatOsztalyNeve típusú objektumot adja vissza.
Dependency Hell - a Proxy
Mivel a Magento dependency injectiont használ, ezért kevésbé átgondolt fejlesztések esetén előfordulhat olyan jelenség, hogy A osztály B osztálytól függ, míg B osztály az A osztálytól. Ilyen esetben a Magento kivételt dob és leáll a hibaüzenettel. Ezt a jelenséget hívják dependency hellnek, amikor is az osztályok kölcsönösen egymástól függnek. Ez önmagában már az objektumorientált fejlesztés szabályait is megszegi, hiszen az oda-vissza függőség értelmetlenné teszi a két osztály különválasztását, egybeolvasztásuk pedig a S.O.L.I.D. elveket sértené tovább, amely része az objektumorientált szemléletnek.
A megoldás lehetne egy időigényes és drága refaktorálás is, ahol akár a teljes modult újra kell gondolni, azonban a Magento biztosít erre egy gyors megoldást a Proxy tervezési minta alkalmazásával.
Röviden, a Proxy egy olyan tervezési minta, amely egy adott objektumhoz biztosít hozzáférést.
A proxy osztályok, mint a factory esetében, generált osztályok, a Magento készíti el őket, a fejlesztőnek csak az osztály nevében kell megjelölni, hogy az egy Proxy osztály, pl. SajatOsztalyNeveProxy. A korábbi példánál maradva, ha A osztály B osztálytól függ és B osztály A osztálytól, akkor a hibát a Proxy segítségével úgy lehet elkerülni, hogy az A osztályban a B osztályt BProxy-ként jelöljük meg.
Az ilyen trükközéssel sok időt és pénzt spórolhatunk meg, azonban a Porxy alkalmazása esetén a proxy-zott osztály újra meghívásra kerül, ami un. lazy-loading jelenséget okoz. Vagyis a proxy osztályok tömeges alkalmazása a Magento erőforrásigényét növeli meg.
Az Interceptor tervezési minta
Az Interceptor olyan tervezési minta, amely egy adott szoftver működési folyamatát engedi manipulálni. Magentoban az interceptor osztály a factory osztályokhoz hasonlóan generálódik. Az interceptor osztályokon keresztül valósulnak meg a Magento 2 pluginek, amelyek függvényekre tudnak feliratkozni.

Például egy extra gomb hozzáadása az admin felülethez megtörténhet pluginen, azaz interceptoron keresztül is. Ebben az esetben az interceptor osztály körülveszi a blokkot, amely visszaadja a gombokat, egy egyedi modul pedig felakaszkodhat erre az interceptorra, amellyel a blokk bizonyos publikus függvényei hozzáférhetővé vállnak.
Observer
A Magentot, egyik leginkább meghatározó tervezési minta, az Observer. Az Observer tervezési minta esetén mindig van egy Event osztály, amely az eseményt határozza meg és egy Subscriber osztály, amely feliratkozik az eseményre. Ez a pattern a Magentot teljesen átszövi. Az observerek alkalmazása gyakorlatilag lehetővé teszi, hogy a modulok hozzáférjenek egymás eseményeihez és ez által a kívánt adatokhoz.
Az observerek jelentősen növelik a Magento erőforrásigényét. Például egy termék elmentése kb. 2 másodpercet vesz igénybe, azonban ha kikapcsoljuk a Magento observereit, akkor a mentési folyamat a tizedmásodperc töredékére is lecsökkenhet. Ez azért van, mert termék mentés során számtalan egyéb modul feliratkozik a mentés eseményére, pl. az árszabályokat, készleteket kezelő modulok, amelyek mind tudni szeretnék ha bármi fontos változás történik a termékkel kapcsolatban, hogy aztán az árszabályok, készletadatok, keresési indexek és egyebek a lehető leghamarabb naprakészek legyenek szinkronban maradva a termék adatokkal.
Az Observer tervezési minta jelentős erőforrásigényének ellenére, számtalan előnyt nyújt, hiszen ha minden entitásnak van művelete (pl. mentés, törlés, frissítés) és ezekhez a műveletekhez események tartoznak (pl. mentés előtt, mentés után, törlés előtt, törlés után, stb.), akkor a webáruház üzleti logikája gyakorlatilag a végtelenségig alakítható anélkül, hogy a gyári Magentoban bármilyen módosítást kellene végezni megtörve a kompatibilitást.
Ha vizualizálva akarnánk elképzelni az observereket, akkor az observerek apró járatok a Magento legbelsejébe és ezeken a járatokon keresztül a működésének logikája is módosítható, anélkül, hogy magában a Magento core-ban módosítanánk.
Facade és Mediator
A Facade az egyik legáltalánosabb tervezési minta, szinte minden keretrendszer alkalmazza, sokban hasonlít a Mediatorhoz. A Magentoban ezek a tervezési minták már nincsenek beszabályozva, csupán alkalmazva vannak.

A facade tervezési mintával készült osztály hozzáférést biztosít más osztályokhoz, míg egy mediator osztály a hozzáférésen kívül plusz funkcionalitást is megvalósít. Ilyen patterneket implementál a Magentoba pl. a SessionManager, a FileSystem, az ObjectManager, stb.
Magento Framework és Magento Base
A fentieken kívül még jó pár tervezési minta létezik az objektumorientált programozásban és a Magentoban. Ezeknek a mintáknak az alkalmazása kiemelkedően fontos egy olyan komplex rendszerben, mint amilyen a Magento, ahol maga a szoftver 18 ezer osztályból épül fel és egy-egy oldalbetöltődés során sok ezer objektum keletkezik ezen osztályokból. A Magento számos útmutatást ad a forráskódján keresztül az objektumok tovább építéséhez.
A Magento fejlesztői egy hihetetlenül absztrakt keretrendszert képzeltek, amelyet egyszerűen csak Magento Frameworknek neveztek el és erre építették rá az alapfunkcionalitásokat biztosító modult a Base-t. Minden egyéb modul erre a Magento Base-re épül és igyekszik követni a benne lefektetett szabályokat, köztük az objektumorientált tervezési mintákat.