Tiszta kód - Node.js
Sziasztok,
volt már alkalmam némi ismerettséget kötni a Node.js-el, alapvetően kisebb alkalmazások írására nagyon szeretem használni. Viszont most gondolkodunk egy nagyobb projekt Node.js-ben való megvalósításán, ezzel kapcsolatban lenne egy kérdésem:
lehet Node.js-ben (Javascriptben) szép, tiszta, külön fájlokban szervezett kódot írni?
Előre is köszönök minden építő jellegű hozzászólást, segítséget!
■ volt már alkalmam némi ismerettséget kötni a Node.js-el, alapvetően kisebb alkalmazások írására nagyon szeretem használni. Viszont most gondolkodunk egy nagyobb projekt Node.js-ben való megvalósításán, ezzel kapcsolatban lenne egy kérdésem:
lehet Node.js-ben (Javascriptben) szép, tiszta, külön fájlokban szervezett kódot írni?
Előre is köszönök minden építő jellegű hozzászólást, segítséget!
Természetesen.Miért ne
Miért ne lehetne? Minden adott hozzá.
Pl. itt van egy frame work a Paypaltól. Mi tesztelésre jasmine-t használunk, igaz csak front end oldalon (angular). De egy alap express is szerintem ad annyi kezdőlökést, hogy onnan már nem nehéz tiszta kódot írni.
Nem a framework-okről van
De ez a module exports-os dolog nagyon nem tetszik, ha valamit külön fájlba akarsz kirakni.
Pedig pont ez az egyik
Ezt nézd át: modules
Az eredeti kérdéshez:
Ha homogén kódminőségű alkalmazást akarsz, akkor ne sajnáld az időt, és ne használj külső modulokat. Sajnos sok fellelhető modul, nem igazán "tiszta kód"-ra épül. Kezdetben nagy lendületet tud adni a projektnek, de mikor a teljesítmény teszteknél derül ki, hogy jelentős, 20-30%-os, teljesítmény csökkenés róható fel neki az nagyon drága tud lenni.
Pedig pont ez az egyik
Akkor feltennék pár kérdést:
- mi van akkor ha nem akarom egy modul összes osztályát egy fájlba beleírni? (ne legyen már egy modul 30 osztállyal 5000 sor egy fájlban) Minden egyes fájlt tele kell szemetelnem a module.exports-al, ami ráadásul megtévesztő is, mert az valójában nem egy modul hanem egy osztály (objektum).
- a modelljeim és controllereimet is szemeteljem tele module.exports-al? Szintén nem modulok. Vagy rakjam őket egy fájlba? Nem túl szép.
- tegyük fel van egy modell, megcsinálom modulként, más húsz osztályban van még használva. Mindenhova be kell require-znem? Mi lesz ha megváltozik a neve? Át kell írni kézzel mind a húsz helyen?
Ez minden, csak nem szépen felépített.
Három éve programozok Node.js-ben, ismerem a modulokat. :) Viszont én még mindig nem látom, hogy alkalmas lenne _szépen felépített_ nagyobb alkalmazások megírására, úgy, hogy egy karbantartható, mások számára is egyszerűen értelmezhető kód legyen a végeredmény.
tegyük fel van egy modell,
Mégis mi korlátoz abban téged, hogy írj vagy használj egy DI frameworkot?
- mi van akkor ha nem akarom
Mindegyik osztályát kirakod külön modulba, a modulod könyvtárába.- a modelljeim és
Más nyelvekben is teleszemeteled. Nem tudom, ez mit változtat.
Igen, ahogy más nyelvekben is.
Mesélj egy nyelvről, amiben jobban van megvalósítva a fenti pár probléma!
Máshol nem tudom, mi a
Demo talán holnap, ha nem leszek lusta, mint általában.
Mindegyik osztályát kirakod
Na ez az, egy osztály mióta egy modul?
Nem, más nyelveken csinálok egy osztályt egy fájlba, és onnantól kezdve fantasztikusan működik a dolog. Nem kell azt még külön egy modulként is definiálnom.
Mesélj egy nyelvről, amiben jobban van megvalósítva a fenti pár probléma!
Nem, más nyelveken nem kell egy osztályt egy külön modulként is definiálnom, nem kell külön behúznom egy másik fájlban, ezzel kvázi belehardcodeolnom a fájl nevét (más nyelveknél van ugye autoloader). Ruby, PHP, Java, csak pár nyelv, ahol jobban van megvalósítva a probléma.
Na ez az, egy osztály mióta
A CommonJS szemantikájában egy modul az, amit kiexportálsz. Ez lehet egyetlen függvény (konstruktor vagy valami más), több függvény (~több osztály, vagy statikus függvények, mint pl az underscore esetében). Modul bármi lehet, akár egy tömb is. A lényeg, hogy valami olyasmi, ami újrafelhasználható.
Hogy egy programozási
Az eredeti kérdésedre még mindig az a válaszom, hogy nodejs-ben (és Javascript-ben is) lehet szép, tiszta, külön fájlokban szervezett kódot írni. Ehhez jól kell ismerni a nodejs-t a javascritet, mvc, tervezési mintákat, ..., és még sok minden mást. Ja és a legfontosabb: legyen idő tervezésre stb. Ne kutyafuttában kelljen dolgozni.
Gondolom ezekkel nem mondtam semmi újat. Ezeket bármelyik programozási nyelvről el elehet mondani.
Az, hogy a nodejs lehetne-e jobb, biztosíthatna kifinomultabb eszközöket, arra viszont egyértelmű igen választ adnék. No, de melyik programnyelvről nem tudnánk ezt mondani?...
Eddigi hozzászólásaidból arra következtetek, hogy kétségeid vannak az felől, hogy azt a nagyobb projektet nodejs-ben meg lehet-e valósítani. Ez esetben viszont érdemes elgondolkodni más alternatívákon. Lehet éppen a java lenne a legalkalmasabb. (A java egyébként biztosan veri teljesítményben a nodejs-t)
a nagyobb projektet
Valószínűleg akkor mélyebb architekturális ismeretek szükségesek, esetleg el kell vonatkoztatni a korábbi módszerektől, és egy radikálisan különbözőt kell megvalósítani, ami jobban illik az adott programozási nyelvhez. Nem véletlen, hogy több JavaScript és Node.js konferencián is ezek a témák, és úgy tűnik a Microservices lehet az egyik befutó architektúra nagyobb projektek esetén (és nem csak Node.js projektek esetén).
egy radikálisan különbözőt
Nem a csavarhúzó hibája, ha
Egyik sem
Minden nyelv más feladatokra használható ideálisan, illetve máshogy illik őket használni. Haskell-ben is írhatnál Windows-os asztali alkalmazásokat, de még sem teszik, mert nem igazán alkalmas a feladatra.
Még szerencse, hogy van választék, hogy melyik feladatra, melyik nyelvet használja az ember.
Nem jók a példáid. Az egy
A node.js-ben viszont van pár olyan hozott kötöttség (vagy incidental complexity), ami rád kényszerít egy bizonyos stílust. Az egyik ilyen a callback-es programozás. Egy átlagos programozási nyelvben megszokott, hogy használható a következő formula:
valtozo = fuggveny(parameterek);
. Node alatt, ha a függvény IO-zik, akkor ez ebben a formában nem oldható meg (azaz elképzelhető, hogy már létezik rá valami, de itt jön be a következő probléma, hogy mi van, ha az általad használt külső modulok nem, akkor ugyanott vagy, ahol a part szakad).A másik ilyen, amit webproghu is felvet, hogy nincs a megszokott include-olásra beépített lehetőség, ami minden normális nyelvben ott van, csak modulokat tudsz betölteni. A különböző kerülő megoldások nem játszanak.
Az egy dolog, hogy az OOP-vel
Az egy dolog, hogy egyes embereknek problémái vannak az aszinkron programozással, de szerencsére vannak más nyelvek is, amikben ez nem probléma (vagy csak nem veszik figyelembe).
Tudsz példát hozni erre, ami nem PHP?
Szerintem a modul eléggé egyértelmű kódfelbontási alap. Van egy modulom, az implementál valamit, és ad egy interface-t, amivel használhatom, ami minden esetben egy objektum. Mivel a függvények is objektumok, ezáltal lehet egy konstruktor is.
Ezzel nem értem, mire akarsz célozni. Milyen kerülő megoldások?
Lásd 11-es hozzászólás.
Nem értem
Java-ban nincs autoloader igazából, ott a fordító valósítja meg a fájlok behúzását az import-ok alapján (amik ugye hardcode-oltak).
PHP-ban csak pár éve van működő autoloader, és az sem tökéletes, ahogy hallottam.
Egyébként a fájl nevét modulok esetén más nyelvekben is be kell hardecode-olni, még tudtommal Python esetében is.
Lásd 7-es hozzászólás Go-ban
Go-ban például egy modul egy könyvtárat jelent, abból beinclude-olja a fájlokat, amikben akár külön osztályok lehetnek.
Modul
"PHP-ban csak pár éve van
11 éve :)
"és az sem tökéletes, ahogy hallottam"
Hallani sok dolgot lehet. :) PHP-val 9 éve foglalkozom, munkahelyen 6 éve fejlesztőként, abból 1 éve vezető fejlesztőként. Eddig sem nekem, sem bármelyik munkatársamnak nem volt gondja az autoloaderrel.
De visszakanyarodva a fő témához:
bár lehetőséget ad rá a Node.js (Javascript), hogy több osztályt (objektumot) egy fájlba írjak (bár aki valaha is foglalkozott tiszta kóddal, az tudja, hogy ez ejnye bejnye), de tegyük fel, hogy én szeretnék megmaradni a szép, áttekinthető egy osztály egy fájl koncepciónál. Maga az osztály ad kifelé egy interfészt, publikus adattagokkal és metódusokkal, ez az OOP egyik lényegi eleme is. Innentől kezdve nekem mi a francért kell még teleszemetelnem az osztályom kódját egy ilyen module exports-os dologgal? Ráadásul még egyszer meg kell adnom, hogy az adott modul/fájl/osztály milyen interfészt nyújt kifelé.
Továbbá ha olyan perverz vágyaim lennének, hogy csinálok egy tényleges (több osztályt összefoglaló modult), akkor lényegében lesz egy modulom, amin belül lesz sok másik modulom, amik valójában osztályok, nem is modulok.
Ne érts félre, nem elvakult támadásba kezdtem a Node.js-el szemben, vagy bárminemű hitvitát szeretnék generálni. De én nem látom azt, hogy ez szép és tiszta lenne. Az is lehet, hogy tévedek, vagy csak nincs még elég tapasztalatom vele. De cáfold meg kérlek a fenti gondolatmenetemet, hogy ezt nem így kell csinálni. Nagyon örülnék neki, mert már nagyon szívesen átnyargalnék Node-ra, de ez a modulos dolog minden, de nem átlátható és/vagy szép.
"Szerintem a modul eléggé
Tehát az objektum is ad kifelé egy jól definiált interface-t, ami ugye az OOP egyik alapja, és még a modul is? Ez szép?
Az aszinkron működésnek
Az include problémát nem értem, a node a CommonJS szintaktikát használja modulok definiálására, a
require
pedig.js
vagy.json
fájlokat tud betölteni, amik lehetnek abszolút/relatív útvonalak (/path/to/file
vagy./path/to/file
), core modulok (http
,net
, stb.) vagy ha ezek közül egyik sem, akkor megpróbálja megkeresni a./node_modules
könyvtárban, ha ott nincs, akkor egy szinttel feljebb a../node_modules
-ben (egészen a gyökérig). Ezek nem kerülő megoldások.Kíváncsi leszek mi fog történni, ha stabil lesz a v8 module implementációja, eddig ehhez sajnos mindenféle transpilereket kellett használni:
Az aszinkron működés egy
fork()
-ot használnak az új folyamat indítására, ott nagy lehet a memóriafoglalás, ugyanis forkoláskor lemásolják az addigi program teljes memóriáját. Ahol szálkezelést (thread) használnak, az jóval olcsóbb, mivel memóriát nem másolnak, az közös (és itt jön be a bonyolítás, hogy lockolni kell, szóval ez az ár). Márpedig a modern Apache például már szálakkal dolgozik.Nem, ez egy incidental
Minden kéréshez egy processzt elindítani (helló CGI!)... Manapság ezt maximum megmosolyogná az ember. Minden kéréshez saját szál indítása (pl. a php apache modul) szintén elég pazarló: egyrészt egy thread indítása nagyon lassú (le kell foglalni a stacket, rendszerhívással regisztrálni kell a szálat, stb), másrészt sok memóriát foglal (az én laptopomon egy szálnak 8MB-os stackje van). A lassú indításon lehet segíteni a threadpoolokkal (ahogy én tudom az apache is valami ilyesmit csinál mod_php címszó alatt), amikor előre elindítunk valahány szálat, és a beérkező kéréseket ezek szolgálják ki.
Az aszinkron működés gyakorlatilag ingyen van, a háttérszálak elvégzik a rendszerhívásokat, az eseményhurok pedig tud pörögni.
pl. a GUI rendszerek mind a
A szerveroldali kérések
ulimit -n
) miatt volt.A javascript szabvány szerint
Melyik szabvány ez? Az alap ECMAscript? Meg tudnád mutatni hol van ez leírva? Vagy a w3c JS API-ra gondolsz? Akkor mi van a web workers-szel?
Web Worker
Persze, hogy nem része. Ezért
Tudtommal nincs semmi olyan
Csak annyi, hogy nincs mód újabb szálak nyitására, és semelyik függvény sem szál-biztonságos (és nem is lehet nyelvi eszközökkel azzá tenni).
Csak annyi, hogy nincs mód
Milyen nyelvi elemekre gondolsz? Egyedül az async / await jut eszembe C#-ból, de előtte is alkalmas volt többszálú programozásra, vagy nem?
Mutex, szemafor, stb. Az
Web workers
Többszálú működéshez kell módszer amivel szálakat tudsz létrehozni. C/C++ esetén eléred a OS-t, annak meghívhatod függvényeit, hogy új processzeket, szálakat nyiss. Ugyanez nem létezik JavaScript esetén.
Nem is mondtam, hogy a nyelv
Ez is pont ugyan annyira nyelvi elem, mint a web workers.
Mutex is csak egy algoritmus. Onnantól kezdve, hogy egy nyelv turing teljes. Azt valósítasz meg benne, amit akarsz.
Mutex is csak egy algoritmus.
Javascript, mint nyelv, ugyan
Nem azt mondtam, hogy
Nem az volt a célom az
A fenti példádban ált. egy sima változót közös memóriában máshol is az állítja be, aki éppen hozzáfér, de némi tervezéssel, szemaforok nélkül is simán megoldható a helyzet, mondjuk valamilyen busy waiting-gel(Pl. Peterson-algoritmus).
Továbbá 'nyelvi szinten' sem vagy teljesen elveszve, pl. web workers-nél pontosan le van írva mi történik a memóriaképpel.
Továbbá 'nyelvi szinten' sem
Csak a web workers nem része a JavaScriptnek, hanem az egy böngészőbeli API, és a böngésző oldja meg a többszálúságot, vagy busy waiting-et.
Ezért raktam zárójelbe.
Elmondom nagy vonalakban, hogy én hogyan látom ezt az egészet. Van két lehetőség: vagy te kezeled a szálak processzoridejét vagy a kernel. Ha az első a helyzet, akkor szükséged lesz egy könyvtárra, ami olyan nyelvi primitíveket aka assembly utasításokat biztosít számodra, mint a TLS vagy egyéb nyalánkságok. Az ilyen könyvtáraknak pedig kb. semmi köze magához a nyelvhez, mivel ezt a platform biztosítja. Ha kernelre van bízva a dolog, akkor bőven elég neked egy kő egyszerű busy waiting, mivel így biztos nem gányol a kritikus részekkel. Tehát egyedül, amire neked nyelvi szinten szükséged van, hogy az egészet összehangolt, az az, ami egy nyelvet Turing-gépnek megfelelővé tesz.
C vagy JS éppen ugyanannyira
Mind a fordító, mind a proci
Ha a fordító ezt nem a specifikáció szerint teszi az egyszerűen bug, az ECMAScript-ben is megtalálható ez execution contexts cím alatt.
Ezt is úgy szint inkább bugnak mondanám, kb. mintha a GC eldobná az adott memóriaszegmenst, függetlenül a referenciaszámlálótól.
A kérdés itt szerintem teljesen más, az emberek keverik a szezont a fazonnal. Egy adott konkrét megvalósítás nem egyenlő a nyelvvel, ami egy absztrakt entitás.
Ezt is úgy szint inkább
Ezt konkrétan csinálják a
Igazából ilyennel még nem találkoztam. Pontosan Te milyen nyelven, esetben futottál bele ilyen problémába?
Ez erősen eltájolt volt tőlem, elnézést. :-)
Mint már fentebb is írtam, nem célom bebizonyítani, hogy JS-ben már pedig megéri többszálú programokat írni, csak annyi, hogy alkalmas rá, bizonyos szinten. Tehát én nem tudom értelmezni azt, hogy a nyelv egyszálú, főleg úgy, hogy én nem látok erre vonatkozóan explicit kijelentést a tervezőktől.
Igazából ilyennel még nem
Javaban kicsit trükkösebb a helyzet, ott pl ha egy T1 szál növel egy countert egy ciklusban, egy T2 szál pedig busy waitel amíg a counter el nem ér egy meghatározott értéket, akkor szintén végtelen ciklus lehet az eredmény. Itt mivel a JIT-nek van context infója, fontos hogy kicsit várni kell (pl sleep(1)), hogy a compiler azt lássa, hogy a ciklus egy hotspot és az adott counterre senki más nem tart referenciát a T1-en kívül. Ekkor be fogja optimalizálni ő is a c-hez hasonlóan, és T2 végtelen ciklusba kerül.
A fenti problémára mindkét nyelvben megoldás, ha pl a változókat volatile-ként definiáljuk, vagy atomi műveleteket használunk. A volatile kicsit mást jelent a 2 nyelvben, de ebből a szempontból azonos a működésük.
Ezek az esetek elég laborszagúak, a valóságban sokkal trükkösebben ütheti fel a fejét az ilyenfajta bug.
Bár közben már válaszoltak,
A node-ben is limitálva van,
Nem ismerem a node.js
Az a gond, hogy a module-nál
Egy module-ba olyan közepes egység kell, hogy belemenjen szerintem, tehát mondjuk max 1000 sor. Terszmészetesen használhatsz más rendszert is, pl coffee, typescript, bla-bla, ami js-t generál, úgy talán nem kell ezzel törődni, nem tudom azok hogy vannak belőve, de simán lehet írni build-et, ami megoldja a csomagolást. A module kb ugyanaz, mint a package/namespace máshol.
Egy module-ba olyan közepes
A címben nem véletlenül tüntettem fel a tiszta kódot. Aki találkozott már ezzel a fogalommal, olvasott a témában könyveket (van konkrétan egy Tiszta kód nevezetű) az tudja, hogy egy 1000 soros fájl nem igazán sorolható ebbe a kategóriába.
Az egy dolog, hogy a könyv
Az addig oké, amíg egy ember
De ha egy fejlesztőcsapat, vagy teszem azt te átveszed az én projektemet, akkor szerinted mekkora lesz annak a költsége, amíg te átlátod/megérted az általam írt 3-5-10 ezer soros kódot egy fájlban?
A könyvben nem véletlenül írják azt amit, a szakmában sok év alatt kialakultak bizonyos konvenciók, amelyek azt hivatottak elősegíteni, hogy szép/tiszta/átlátható kódbázis készüljön.
De csak, hogy példával támasszam alá:
tavaly egy funkcionalitását tekintve viszonylag egyszerű ügyviteli rendszeren kellett lecserélnünk a felületet, ami pont így volt felépítve, fájlokba belezsúfolva 1-2-3-10 ezer sornyi kód. Fél évbe telt... egyszerűen a fejlesztők idejének nagy része azzal ment el, hogy keresgessék összevissza a több ezer soros fájlokban, hogy a korábbi fejlesztő mégis hova rakott egy adott függvényt, és az milyen más függvényekkel áll még kapcsolatban. Ezért nem lehet az egész egy fájlban. Kivétel persze, ha az ember magának programozgat egyedül, hobbiból. De egy cégnél nem mindegy, hogy egy régebbi projekt továbbfejlesztésekor 2 fejlesztő hat hónapnyi munkabérét áldozza a dologra, vagy 1 fejlesztő 2 hónapnyi munkabérét. (6 millió forint, vagy 1 millió forint)
Elfogadom, hogy van egy
Az az idő rég elmúlt, amikor vi-t használtak a nagy projekteken dolgozó fejlesztők (remélem).
Ha meg egy normális IDE-t használ az ember, akkor gyakorlatilag édesmindegy, hogy egy fájl vagy sok apró.
Rosszul látom?
(eredetileg nagyjából ugyanazt akartam válaszolni Gábornak, amit te is írtál, szinte szó szerint. De eszembe jutott, hogy mi mindent tudnak az olyanok is mint, az eclipse vagy a frissen előszedett pycharm és máris nem látom ennyire egyértelműnek ezt a korlátozást)
Egy normális IDE használata
Na épp erre mondom, hogy
Ott már nem vagy rákényszerítve igazán, hogy forráskódban turkálj, ott már osztályokban, metódusokban, függvényekben stb. tudsz navigálni, keresgélni.
Az IDE analizálja, felbontja, könnyen értelmezhetővé teszi akár a több tízezer soros kódot is.
Azért nézd meg, hogy a Clean Code mikor íródott eredetileg! És nézd meg, hogy mennyit fejlődtek ezek az eszközök!
IDE ide vagy oda ( :) ), de a
Szerintem - ha egy kellőképp
A munkának ez a része már elég jól automatizált ahhoz, hogy ha nem akarsz később emacs/vi/ed segítségével turkálni a forrásban, akkor ne legyen feltétlen szükséged a kisméretű forrás fájlokra.
Ha elolvasod még egyszer a
Azért írtam a modulokat max 1000 sornak, mert azokban még össze lehet szedni 1-2 magas szintű osztályba a felületet, amin keresztül a többi modul eléri az adott funkciót, így nem áll fenn az a veszély, hogy a végeredmény átláthatatlan lesz. Elég elolvasni az első osztályt, hogy lásd, hogy mit csinál a kód, aztán ha az is érdekel, hogy hogyan csinálja, akkor tovább scrollozhatsz.
Na mindegy, nekem ez a véleményem, de egyelőre még én is küzdök a js-el, tiszta kód terén. Majd ha van kiforrott megoldásom a témára, akkor visszajelzek.
Na mindegy, nekem ez a
Azt megköszönöm előre is. Lehet tudni egyébként hol jársz a küzdelemben? Ha úgy akad szívesen konzultálnék az ügyben, vannak nekem is ötleteim, de nem tudom még jó irányba haladok-e.
Hát én most jelenleg itt
Én sem értem
Ha ismered a belépési pontot (ahol a feldolgozás indul), nem mindegy, hogy egy fájlon belül ugrálsz a függvények között, vagy pedig százban?
Most nem vitatkozni szeretnék veled, csak megérteni.
Az én szerkesztőmben lehet olyat csinálni, hogy egy fájl ablakát kétfelé szedni, így ugyanannak a kódnak két részletét láthatom egymás alatt.
Az egész inkább arról szól,
illetve itt feltennem azt a
Rosszul tervezett kódnál
A címben nem véletlenül
Mekkora lehet egy modul? Van értelme egyáltalán azon vitatkozni, hogy hány soros egy modul? Amúgy ajánlom a héten beküldött blogmarkomat, ami a kifejezetten extrém eseteket boncolgatja (modul méret szempontjából).
Attól függ, hogy hogyan
Ki tudnád fejteni, hogy ha
- a modulom tartalmaz több
- vagy csak utálom, ha egy fájl 12526 sorból áll
- stb...
Pl. ha mondjuk egy smtp modult akarok csinálni, akkor nem lenne szerencsés mindent egy fájlba írni.
Ok
Imho az aszinkron része a