hogyan lehet kijavítani a csúnya körkörös függőségi problémákat egyszer és mindenkorra a JavaScript & TypeScript programban

  1. index.js szükséges: AbstractNode.js
  2. a modulbetöltő megkezdi a AbstractNode.js betöltését és a modul kódjának futtatását. A dolog, amellyel először találkozik, egy require (import) utasítás a Leaf
  3. – hez, így a modulbetöltő elkezdi betölteni a Leaf.js fájlt. Ami viszont azzal kezdődik, hogy Abstractnode.jsszükséges.
  4. AbstractNode.js már be van töltve, és azonnal visszatér a modul gyorsítótárából. Mivel azonban ez a modul még nem futott túl az első soron (a Leaf követelménye), a AbstractNode osztályt bevezető utasítások még nem kerültek végrehajtásra!
  5. tehát a Leaf osztály megpróbálja kiterjeszteni a undefined értéket, nem pedig egy érvényes osztályt. Amely a fent bemutatott futásidejű kivételt dobja. Bumm!

Fix kísérlet 1

tehát kiderül, hogy körkörös függőségünk csúnya problémát okoz. Ha azonban alaposan megvizsgáljuk, elég könnyű meghatározni, hogy mi legyen a rakodási sorrend:

  1. töltse be a AbstractNode osztály Első
  2. töltse be a Node és Leaf osztály után.

más szavakkal, először definiáljuk a AbstractNode osztályt, majd tegyük szükségessé a Leafés Node osztályt. Ennek működnie kell, mert a Leaf és Node osztály meghatározásakor még nem kell ismerni őket. Mindaddig, amíg meg vannak határozva, mielőtt a AbstractNode.from – et először hívják meg, rendben kell lennünk. Tehát próbáljuk meg a következő változást:

kiderül, van néhány probléma ezzel a megoldással:

először is, ez csúnya és nem méretezhető. Egy nagy kódbázisban ez azt eredményezi, hogy az import véletlenszerűen mozog, amíg a dolgok csak működnek. Ami gyakran csak átmeneti, mivel egy kis refaktorálás vagy az importálási nyilatkozatok módosítása a jövőben finoman módosíthatja a modul betöltési sorrendjét, újra bevezetve a problémát.

másodszor, hogy ez működik-e, nagymértékben függ a modulcsomagolótól. Például a codesandbox alkalmazásban, amikor alkalmazásunkat csomaggal (vagy Webcsomaggal vagy összesítéssel) csomagoljuk, ez a megoldás nem működik. Ha azonban ezt helyben futtatja a csomóponttal.js és commonJS modulok ez a megoldás jól működhet.

a probléma elkerülése

tehát nyilvánvalóan ezt a problémát nem lehet könnyen megoldani. Elkerülhető lett volna? A válasz igen, a probléma elkerülésének számos módja van. Először is, megtarthattuk volna a kódot egyetlen fájlban. Amint az a kezdeti példánkban látható, így megoldhatjuk a problémát, mivel teljes ellenőrzést biztosít a modul inicializálási kódjának sorrendje felett.

másodszor, egyesek a fenti problémát érvként használják olyan állítások megfogalmazására, mint “nem szabad osztályokat használni” vagy “ne használja az öröklést”. De ez a probléma túlzott egyszerűsítése. Bár egyetértek azzal, hogy a programozók gyakran túl gyorsan folyamodnak az örökléshez, bizonyos problémák esetén ez csak tökéletes, és nagy előnyökkel járhat a kódszerkezet, az újrafelhasználás vagy a teljesítmény szempontjából. De ami a legfontosabb, ez a probléma nem korlátozódik az osztály öröklésére. Pontosan ugyanaz a probléma vezethető be, ha körkörös függőségek vannak a modulváltozók és a modul inicializálása során futó függvények között!

újraszervezhetjük a kódunkat oly módon, hogy a AbstractNode osztályt kisebb darabokra bontjuk, így AbstractNode-nek nincs függősége Nodevagy Leaf – tól. Ebben a homokozóban a from metódus kihúzta a AbstractNode osztályt, és egy külön fájlba helyezte. Ez megoldja a problémát, de most a projektünk és az API másképp van felépítve. Nagy projektekben nagyon nehéz lehet meghatározni, hogyan lehet ezt a trükköt kihúzni, vagy akár lehetetlen! Képzeljük el például, mi történne, ha a print módszer függ Node vagyLeaf a következő iteráció a mi app…

bónusz: egy további csúnya trükk, amit korábban használtam: vissza bázis osztályok függvények és tőkeáttétel funkció emelő, hogy a dolgok betöltött a megfelelő sorrendben. Nem is tudom, hogyan magyarázzam el rendesen.

a belső modul minta a mentéshez!

számos projekt során többször is küzdöttem ezzel a problémával, néhány példa a Mendix, a MobX, a MobX-state-tree és számos személyes projekt munkája. Néhány évvel ezelőtt még egy szkriptet is írtam az összes forrásfájl összefűzésére és az összes importálási utasítás törlésére. A szegény mans modul bundler csak azért, hogy egy fogást a modul betöltése érdekében.

a probléma néhányszor történő megoldása után azonban megjelent egy minta. Az egyik, amely teljes mértékben ellenőrzi a modul betöltési sorrendjét, anélkül, hogy át kellene alakítania a projektet, vagy furcsa hackeket kellene húznia! Ez a minta tökéletesen működik az összes eszközlánccal, amelyen kipróbáltam (összesítő, Webcsomag, csomag, csomópont).

ennek a mintának a lényege egy index.js és internal.js fájl bevezetése. A játék szabályai a következők:

  1. a internal.js modul mind importál, mind exportál mindent a projekt minden helyi moduljából
  2. a projekt minden más modulja csak a internal.js fájlból importál, és soha nem közvetlenül a projekt többi fájljából.
  3. a index.js fájl a fő belépési pont, és importál és exportál mindent a internal.js – ből, amit ki szeretne tenni a külvilágnak. Ne feledje, hogy ez a lépés csak akkor releváns, ha mások által fogyasztott könyvtárat tesz közzé. Tehát kihagytuk ezt a lépést a példánkban.

vegye figyelembe, hogy a fenti szabályok csak a helyi függőségekre vonatkoznak. Külső modul import marad, ahogy van. Végül is nem vesznek részt a körkörös függőségi problémáinkban. Ha ezt a stratégiát alkalmazzuk a demo alkalmazásunkra, a kódunk így fog kinézni:

amikor először alkalmazza ezt a mintát, nagyon mesterkéltnek érezheti magát. De van néhány nagyon fontos előnye!

  1. először is megoldottuk a problémánkat! Amint azt itt bemutatjuk, alkalmazásunk boldogan fut újra.
  2. ennek oka, hogy ez megoldja a problémánkat: most már teljes mértékben ellenőrizzük a modul betöltési sorrendjét. Bármi legyen is a internal.js importálási sorrend, a modul betöltési sorrendje lesz. (Érdemes ellenőrizni az alábbi képet, vagy olvassa el újra a fenti modulrendelési magyarázatot, hogy megtudja, miért van ez így)
  3. nem kell olyan refaktorokat alkalmaznunk, amelyeket nem akarunk. Azt sem vagyunk kénytelenek használni csúnya trükköket, mint a mozgó igényel nyilatkozatok alján a fájlt. Nem kell veszélyeztetnünk a kódbázis architektúráját, API-ját vagy szemantikai szerkezetét.
  4. bónusz: az importálási utasítások sokkal kisebbek lesznek, mivel kevesebb fájlból importálunk dolgokat. Például a AbstractNode.js csak az importálási utasításban van, ahol korábban kettő volt.
  5. bónusz: aindex.js segítségével egyetlen igazságforrásunk van, amely finom szemcsés ellenőrzést biztosít a külvilág számára.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.