7 minta a Refactor Fat ActiveRecord modellekhez - Kód klíma

Október 17. 8 perc olvasás

minta

7 minta a Refactor Fat ActiveRecord modellekhez

Sasha
Rezvina

Ossza meg

kapcsolódó címkék

Amikor a csapatok a Code Climate használatával javítják a Rails alkalmazások minőségét, megtanulják megszüntetni azt a szokást, hogy a modellek meghíznak. A „kövér modellek” karbantartási problémákat okoznak a nagy alkalmazásokban. Csak fokozatosan jobbak, mint a tartománylogikájú vezérlők zsúfoltsága, ezek általában kudarcot jelentenek az egységes felelősség elvének (SRP) alkalmazásának. A „bármi, ami a felhasználó tevékenységével kapcsolatos”, nem egyetlen felelősség.

Korán az SRP könnyebben alkalmazható. Az ActiveRecord osztályok kezelik a kitartást, az asszociációkat és nem sok mást. De apránként nőnek. Azok az objektumok, amelyek eredendően felelősek a kitartásért, minden üzleti logika tényleges tulajdonosává válnak. Egy-két év múlva van egy felhasználói osztálya, több mint 500 soros kóddal és több száz módszerrel a nyilvános felületen. Visszahívási pokol következik.

Amint még bonyolultabbá (olvassa el: funkciók!) Ad hozzá alkalmazását, a cél az, hogy elosztja apró, beágyazott objektumok (és magasabb szinten modulok) összehangolt halmazán, ahogyan a torta tésztát is eloszthatja serpenyő alja. A zsírmodellek olyanok, mint a nagy csomók, amelyeket akkor kapunk, amikor először beleöntjük a tésztát. A refaktor megbontja őket, és egyenletesen elosztja a logikát. Ismételje meg ezt a folyamatot, és egy egyszerű, jól definiált interfésszel rendelkező objektum együttese valós szimfóniában fog működni.

Lehet, hogy gondolkodik:

"De a Rails miatt nagyon nehéz megtenni az OOP-t!"

Régen ezt is elhittem. De némi feltárás és gyakorlat után rájöttem, hogy a Rails (a keret) nem akadályozza annyira az OOP-t. A Rails „konvenciói” nem skálázódnak, pontosabban a komplexitás kezelésére vonatkozó konvenciók hiánya azon túl, amit az Active Record minta elegánsan kezel. Szerencsére ott alkalmazhatjuk az OO-alapú elveket és a legjobb gyakorlatokat, ahol a Rails hiányzik.

Ne vonja ki a mixeket a zsírmodellekből

Hagyjuk ezt az útból. Bátorítom a módszerek halmazának a nagy ActiveRecord osztályból való kivonulását „aggodalmakra” vagy modulokra, amelyeket aztán csak egy modellbe keverünk. Egyszer hallottam valakit mondani:

"Bármely alkalmazás/aggodalomjegyzéket tartalmazó alkalmazás aggályos."

És egyetértek. Az összetétel helyett az öröklést részesítse előnyben. Az ilyen keverők használata hasonló egy rendetlen szoba „megtisztításához” azzal, hogy a rendetlenséget hat különálló ónfiókba dobja és bezárja. Persze, tisztábban néz ki a felszínen, de a szemétfiókok valójában megnehezítik a doménmodell tisztázásához szükséges bontások és kivonatok azonosítását és végrehajtását.

Most pedig az átalakításokról!

1. Bontsa ki az értékobjektumokat

Érték Az objektumok egyszerű objektumok, amelyek egyenlősége nem az identitás, hanem az értéküktől függ. Általában megváltoztathatatlanok. A dátum, az URI és az elérési út példák a Ruby szabványos könyvtárából, de az alkalmazás képes (és szinte biztosan meg kell határoznia) tartományspecifikus Értékobjektumokat is. Az ActiveRecords-ból való kivonás alacsony függesztésű, refaktoráló gyümölcs.

A Rails szolgáltatásban az Értékobjektumok nagyszerűek, ha van olyan attribútum vagy attribútumok kis csoportja, amelyekhez logika társul. Az alapszöveges mezőkön és számlálókon kívül bármi más lehetséges az Értékobjektum kibontására.

Például egy olyan szöveges üzenetküldő alkalmazásban, amelyen dolgoztam, volt egy Telefonszám-érték objektum. Az e-kereskedelmi alkalmazásnak pénzosztályra van szüksége. A Code Climate rendelkezik egy Értékelés nevű értékobjektummal, amely egy egyszerű A - F osztályzatot képvisel, amelyet minden osztály vagy modul megkap. Használhattam (és eredetileg használtam is) egy Ruby karakterláncot, de a minősítés lehetővé teszi számomra, hogy kombináljam a viselkedést az adatokkal:

Ezután minden ConstantSnapshot nyilvánosságra hozható felületen tárolja a Rating egy példányát:

A ConstantSnapshot osztály lecsökkentése mellett ennek számos előnye van:

  • A #világ_than? és #jobb_? a módszerek kifejezőbb módszert kínálnak az értékelések összehasonlítására, mint a Ruby beépített operátorai (pl. és>).
  • A #hash és az #eql meghatározása? lehetővé teszi a minősítés kivonatként történő használatát. A Code Climate ezt az állandók idiomatikusan csoportosítja besorolásaik alapján az Enumerable # group_by segítségével .
  • A # to_s metódus lehetővé teszi számomra, hogy minden további munka nélkül interpoláljam a minősítést egy karakterláncba (vagy sablonba).
  • Az osztálydefiníció kényelmes helyet biztosít a gyári módszer számára, és a megfelelő besorolást adja vissza egy adott „helyreállítási költséghez” (az a becsült idő, amely egy adott osztály összes szagának megszüntetéséhez szükséges).

2. Bontsa ki a szolgáltatási objektumokat

A rendszer egyes műveletei indokolttá teszik egy szolgáltatásobjektum működésének beágyazását. Akkor nyúlok a Szolgáltatási objektumokhoz, ha egy művelet megfelel az alábbi kritériumok közül egynek vagy többnek:

  • A művelet összetett (pl. Könyvelés lezárása egy elszámolási időszak végén)
  • A művelet több modellen keresztül érhető el (pl. E-kereskedelmi vásárlás a Order, a CreditCard és az Ügyfél objektumok használatával)
  • A művelet kölcsönhatásba lép egy külső szolgáltatással (pl. Közzététel a közösségi hálózatokon)
  • A művelet nem az alapul szolgáló modell alapvető problémája (pl. Elavult adatok söpörése egy bizonyos időszak után).
  • A művelet végrehajtásának több módja van (pl. Hitelesítés hozzáférési tokennel vagy jelszóval). Ez a Négyek bandája stratégiai minta.

Példaként kihúzhatjuk a User # hitelesítési metódust a UserAuthenticatorba:

És a SessionsController így néz ki:

3. Bontsa ki az Űrlap objektumokat

Amikor több ActiveRecord modell frissülhet egyetlen űrlap elküldésével, egy Űrlap objektum be tudja zárni az összesítést. Ez sokkal tisztább, mint az acceptepts_nested_attributes_for használata, amelyet szerény véleményem szerint el kellene vetni. Gyakori példa erre a regisztrációs űrlap, amely mind a vállalat, mind a felhasználó létrehozását eredményezi:

Elkezdtem használni a Virtust ezekben az objektumokban az ActiveRecord-szerű attribútumfunkciók megszerzéséhez. A Form Object az ActiveRecord-hoz hasonlóan fut, így a vezérlő ismerős marad:

Ez jól működik az olyan egyszerű esetekben, mint a fentiek, de ha az űrlap kitartási logikája túl bonyolulttá válik, akkor ezt a megközelítést kombinálhatja egy szolgáltatásobjektummal. Bónuszként, mivel az érvényesítési logika gyakran kontextuális, pontosan azon a helyen határozható meg, ahol fontos, ahelyett, hogy magában az ActiveRecord-ban kellene ellenőriznie az érvényesítéseket.

4. Bontsa ki a lekérdezési objektumokat

Az ActiveRecord alosztály definíciójának összetett SQL-lekérdezéseihez (hatókörként vagy osztálymódszerként) fontolja meg a lekérdezési objektumok kibontását. Minden lekérdezési objektum felelős az üzleti szabályok alapján elért eredménykészlet visszaadásáért. Például egy lekérdezési objektum az elhagyott próbák megtalálásához így nézhet ki:

Használhatja háttérmunkában e-mailek küldésére:

Mivel az ActiveRecord: Relation példányok első osztályú polgárok a Rails 3-tól kezdve, nagyszerűen hozzájárulnak egy lekérdezési objektumhoz. Ez lehetővé teszi a lekérdezések összeállítását az összetétel használatával:

Ne zavarjon egy ilyen osztályt külön-külön. Használjon olyan teszteket, amelyek együtt gyakorolják az objektumot és az adatbázist annak biztosítására, hogy a helyes sorok a megfelelő sorrendben kerüljenek visszaadásra, és az esetleges összekapcsolódások vagy lelkes terhelések megtörténjenek (például az N + 1 lekérdezési hibák elkerülése érdekében).

5. Mutassa be a Objektumok megtekintése elemet

Ha a logikára pusztán megjelenítés céljából van szükség, az nem tartozik a modellbe. Kérdezd meg magadtól: "Ha alternatív interfészt telepítenék ehhez az alkalmazáshoz, például egy hanggal aktivált felhasználói felületet, szükségem lenne erre?". Ha nem, fontolja meg egy segítőbe vagy (gyakran jobb) View objektumba helyezését.

Például a Code Climate fánkdiagramjai lebontják az osztályzat besorolását a kódalap pillanatképe alapján (pl. Rails on Code Climate), és nézetként kapszulázzák őket:

Gyakran találok egy-egy kapcsolatot a Views és az ERB (vagy Haml/Slim) sablonok között. Ez arra késztetett, hogy elkezdjem vizsgálni a Railsnél használható Kétlépcsős nézet mintázatát, de még nincs egyértelmű megoldásom.

Megjegyzés: A „Presenter” kifejezés megragadt a Ruby közösségben, de poggyászai és ellentmondásos használata miatt kerülöm. A „Presenter” kifejezést Jay Fieldsto vezette be, amelyet a fentiekben „Form Objects” -nek nevezek. Rails sajnos a „nézet” kifejezést használja az úgynevezett „sablonok” leírására. A kétértelműség elkerülése érdekében ezeket a Nézet objektumokat néha „Modellek megtekintése” néven emlegetem.

6. Bontsa ki a politikai objektumokat

Néha a bonyolult olvasási műveletek megérdemlik a saját objektumaikat. Ezekben az esetekben egy házirend objektumhoz nyúlok. Ez lehetővé teszi, hogy a tangenciális logikát - például azokat, amelyeket a felhasználók elemzési célból aktívnak tekintenek - távol tartsa az alapvető tartományi objektumoktól. Például:

Ez a házirendobjektum egy üzleti szabályt foglal magában, miszerint a felhasználó akkor tekinthető aktívnak, ha megerősített e-mail címmel rendelkezik, és az elmúlt két hétben bejelentkezett. A házirendobjektumokat üzleti szabályok csoportjához is használhatja, például egy engedélyezőhöz, amely szabályozza, hogy a felhasználók mely adatokhoz férhetnek hozzá.

A házirendobjektumok hasonlóak a szolgáltatásobjektumokhoz, de az írási műveletekhez a „Szolgáltatásobjektum” kifejezést, az olvasásokhoz pedig a „Házirendobjektum” kifejezést használom. Hasonlóak a Query Objects-hez is, de a Query Object-ek az SQL végrehajtására összpontosítanak egy eredményhalmaz visszaadására, míg a Policy Objects a memóriába már betöltött tartománymodelleken működnek.

7. Kivonat dekorátorok

A dekorátorok lehetővé teszik a meglévő műveletek funkcionalitását, ezért a visszahívásokhoz hasonló célt szolgálnak. Azokban az esetekben, amikor a visszahívási logikának csak bizonyos körülmények között kell futtatnia, vagy ha a modellbe beillesztené, az túl sok felelősséget jelentene a modell számára, a Decorator hasznos.

Megjegyzés közzététele egy blogbejegyzéshez kiválaszthatja a bejegyzést valakinek a Facebook falára, de ez nem jelenti azt, hogy a logikát erősen be kellene kötni a Megjegyzés osztályba. Az egyik jel, amelyet túl sok felelősséggel ruházott fel a visszahívások során, a lassú és törékeny tesztek vagy a késztetés a mellékhatások elfojtására teljesen független tesztesetekben.

Így nyerheti ki a Facebook hozzászólási logikáját a Dekorátorba:

És hogyan használhatja egy vezérlő:

A dekorátorok abban különböznek a szolgáltatási objektumoktól, hogy a meglévő interfészek felelősségét rétegezik. A díszítés után az együttműködők csak úgy kezelik a FacebookCommentNotifier példányt, mintha egy Megjegyzés lenne. A szokásos könyvtárában a Ruby számos olyan lehetőséget kínál, amelyek megkönnyítik az épületburkolókat a metaprogramozással.

Csomagolás

Még a Rails alkalmazásban is sok eszköz van a modellréteg összetettségének kezelésére. Egyik sem követeli meg, hogy dobja ki a síneket. Az ActiveRecord egy fantasztikus könyvtár, de minden minta elromlik, ha kizárólag ettől függ. Próbálja meg korlátozni az ActiveRecords-ot a kitartás viselkedésére. Szórja meg ezeket a technikákat a tartománymodell logikájának elterjesztése érdekében, és az eredmény sokkal fenntarthatóbb alkalmazás lesz.

Azt is megjegyzi, hogy az itt leírt minták közül sok meglehetősen egyszerű. Az objektumok csupán „Plain Old Ruby Objects” (PORO), amelyeket különböző módon használnak. És ez része az OOP lényegének és szépségének. Minden problémát nem kell keretrendszerrel vagy könyvtárral megoldani, és a megnevezés nagyon sokat számít.

Mit gondol a fent bemutatott hét technikáról? Mik a kedvenceid és miért? Hiányzott nekem? Mondja meg nekem a megjegyzésekben!

P.S. Ha hasznosnak találta ezt a bejegyzést, érdemes feliratkoznia e-mailes hírlevelünkre (lásd alább). Alacsony volumenű, és tartalmat tartalmaz az OOP-ról és a Rails ilyen alkalmazások újrafaktorozásáról.

További irodalom

Köszönet: Steven Bristol, Piotr Solnica, Don Morrison, Jason Roelofs, Giles Bowkett, Justin Ko, Ernie Miller, Steve Klabnik, Pat Maddox, Sergey Nartimov és Nick Gauthier, hogy áttekintették ezt a bejegyzést.