Monkey Patching v dynamických programovacích jazykoch: Príklad JavaScriptu

Javascript
dynamické programovanie
Monkey Patching v dynamických programovacích jazykoch cover image

Úvod

Tento článok bude skúmať koncepty dynamických a statických programovacích jazykov, hlavné rozdiely medzi nimi a to, čo každá paradigma poskytuje z hľadiska výhod a úskalí. Tento prieskum sa ďalej zameria na dynamické programovacie jazyky, najmä na jeden zo základných vzorov, ktoré umožňuje: Monkey Patch, tento vzor bude predstavený pomocou príkladu v JavaScripte.

Dynamické vs statické programovacie jazyky

Terminológia

Aby sme pochopili, čo predstavuje dynamický alebo statický jazyk, musíme pochopiť niekoľko kľúčových pojmov bežne používaných v tomto kontexte: Čas kompilácie, Runtime a *Kontrola typu *.

Kompilácia a Runtime sú dva pojmy, ktoré zodpovedajú rôznym štádiám životného cyklu počítačového programu, počnúc časom kompilácie.

Čas kompilácie

Čas kompilácie je prvým krokom v životnom cykle programu. Vývojár píše kód v danom programovacom jazyku. Stroj často nie je schopný porozumieť kódu napísanému vo vysokoúrovňovom jazyku, takže na jeho preklad do stredného formátu nižšej úrovne, ktorý je pripravený na vykonanie, sa používa špecializovaný kompilátor.

Beh programu

Runtime zvyčajne zahŕňa dva kroky: načítanie programu do pamäte alokáciou zdrojov potrebných na jeho vykonanie spolu s jeho pokynmi a následné spustenie programu podľa poradia týchto pokynov.

Nasledujúci diagram znázorňuje tento proces:

Kontrola typu

Kontrola typu je vstavaná funkcia takmer vo všetkých programovacích jazykoch. Je to schopnosť skontrolovať, či hodnota priradená danej premennej zodpovedá správnemu typu tejto premennej. Každý programovací jazyk má odlišný spôsob reprezentácie hodnoty daného typu v pamäti. Tieto rôzne reprezentácie umožňujú kontrolovať súlad medzi typom hodnoty a typom premennej, ktorej sa pokúšate priradiť túto hodnotu.

Teraz, keď máme znalosti o životnom cykle programu a kontrole typu na vysokej úrovni, môžeme pristúpiť k skúmaniu statických programovacích jazykov.

Statické programovacie jazyky

Statické programovacie jazyky, tiež označované ako staticky typované jazyky, sú jazyky, ktoré používajú kontrolu typu, ktorú sme spomenuli vo fáze kompilácie. To v skutočnosti znamená, že premenná si ponecháva svoj typ z deklarácie a nemožno jej priradiť inú hodnotu ako hodnoty z jej typu deklarácie. Statické programovacie jazyky ponúkajú extra bezpečnosť pri práci s typmi, ale môžu spomaliť proces vývoja v určitých prípadoch použitia, keď sa to stane tvrdým obmedzením.

Dynamické programovacie jazyky

Dynamické programovacie jazyky na druhej strane používajú kontrolu typu za behu. To znamená, že každá premenná môže mať v ľubovoľnom bode programu akúkoľvek hodnotu. To môže byť prospešné, pretože ponúka úroveň flexibility pre vývojárov, ktorá nie je prítomná v statických jazykoch. Dynamické jazyky majú tendenciu byť pomalšie pri vykonávaní ako ich statické náprotivky, pretože zahŕňajú ďalší krok dynamického zisťovania zadávania každej premennej.

Opičia náplasť

Statické verzus dynamické písanie je základnou črtou programovacieho jazyka, pričom jedna paradigma nad druhou môže umožniť množstvo rôznych vzorov a praktík, ktoré môžu výrazne zlepšiť kvalitu a rýchlosť vývoja. Môže tiež otvoriť dvere mnohým obmedzeniam a protivzorom, ak sa pri rozhodovaní o dizajne nevenujú žiadne starostlivé úvahy.

Najmä je známe, že dynamicky typované programovacie jazyky ponúkajú vyššiu úroveň flexibility, pretože neobmedzujú premennú na jeden typ. Táto flexibilita je spojená s nákladmi na dodatočnú zodpovednosť vývojára pri implementácii a ladení programov, aby sa zabezpečilo, že nedochádza k nepredvídateľnému správaniu. Z tejto filozofie pochádza vzor opice.

Monkey Patch označuje proces rozšírenia/zmeny fungovania komponentu za behu. Príslušným komponentom môže byť knižnica, trieda, metóda alebo dokonca modul. Myšlienka je rovnaká: kus kódu je vytvorený na splnenie určitej úlohy a cieľom opičieho záplatovania je zmeniť alebo rozšíriť správanie tohto kusu kódu tak, aby vykonal novú úlohu, a to všetko bez zmeny samotného kódu. .

Toto je umožnené v dynamickom programovacom jazyku, pretože bez ohľadu na to, s akým typom komponentu máme čo do činenia, stále má rovnakú štruktúru objektu s rôznymi atribútmi, atribúty môžu obsahovať metódy, ktoré je možné priradiť na dosiahnutie nového správania v objekte. bez toho, aby sme zachádzali do jeho vnútra a detailov implementácie. Toto sa stáva obzvlášť užitočným v prípade knižníc a modulov tretích strán, pretože tie je zvyčajne ťažšie vyladiť.

Nasledujúci príklad predstaví bežný prípad použitia, ktorý môže ťažiť z použitia techniky opíc. Pre implementáciu tu bol použitý Javascript, ale to by sa malo stále vo všeobecnosti vzťahovať na akýkoľvek iný dynamický programovací jazyk.

Príklad

Implementujte rámec minimálneho testovania s natívnym modulom HTTP Node

Testovanie jednotiek a integrácie môže spadať pod prípady použitia opravy Monkey. Zvyčajne zahŕňajú testovacie prípady, ktoré zahŕňajú viac ako jednu službu na testovanie integrácie alebo závislosti API a/alebo databázy na testovanie jednotiek. V týchto dvoch scenároch a na splnenie cieľov testovania by sme v prvom rade chceli, aby naše testy boli nezávislé od týchto externých zdrojov. Spôsob, ako to dosiahnuť, je zosmiešňovanie. Mocking simuluje správanie externých služieb, takže test sa môže zamerať na skutočnú logiku kódu. Monkey patching tu môže byť užitočný, pretože môže modifikovať metódy externých služieb ich nahradením zástupnými metódami, ktoré nazývame „stub“. Tieto metódy vracajú očakávaný výsledok v testovacích prípadoch, takže sa môžeme vyhnúť iniciovaniu požiadaviek na produkčné služby len kvôli testom.

Nasledujúci príklad je jednoduchá implementácia opravy Monkey na natívnom http module NodeJs. Modul http je rozhranie, ktoré implementuje metódy protokolu http pre NodeJs. Používa sa hlavne na vytváranie barebone http serverov a komunikáciu s externými službami pomocou protokolu http.

V nižšie uvedenom príklade máme jednoduchý testovací prípad, v ktorom zavoláme externú službu na získanie zoznamu ID používateľov. Namiesto volania skutočnej služby opravíme metódu http get, takže len vráti očakávaný výsledok, ktorým je pole náhodných ID používateľov. To sa nemusí zdať veľmi dôležité, pretože iba načítavame údaje, ale ak implementujeme ďalší testovací prípad, ktorý zahŕňa zmenu údajov určitého druhu, mohli by sme náhodne zmeniť údaje o produkcii pri spustení testov.

Takto môžeme implementovať naše funkcionality a písať testy pre každú funkcionalitu a zároveň zaistiť bezpečnosť našich produkčných služieb.

// import the http module
let http = require("http");

// patch the get method of the http module
http.get = async function(url) {
  return {
    data: ["1234", "1235", "1236", "1236"]
  };
}

// example test suite, call new patched get method for testing
test('get array of user ids from users api', async () => {
  const res = await http.get("https://users.api.com/ids");
  const userIds = res.data;
  expect(userIds).toBeDefined();
  expect(userIds.length).toBe(4);
  expect(userIds[0]).toBe("1234");
});

Vyššie uvedený kód je jednoduchý, importujeme modul http, priradíme metódu http.get pomocou novej metódy, ktorá len vráti pole ID. Teraz zavoláme novú oplatenú metódu v testovacom prípade a získame nový očakávaný výsledok.

~/SphericalTartWorker$ npm test

> nodejs@1.0.0 test
> jest

PASS  ./index.test.js
  ✓ get array of user ids from users api (25 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.977 s, estimated 2 s
Ran all test suites.

Bežné úskalia a obmedzenia

Nemalo by byť žiadnym prekvapením, že opice patching má svoje vlastné chyby a obmedzenia. V kontexte modulov v systéme modulov uzla sa záplatovanie globálneho modulu, ako je http, považuje za operáciu s vedľajšími účinkami, pretože http je prístupné z akéhokoľvek bodu v kódovej základni a akákoľvek iná entita môže byť na ňom závislá. Tieto entity očakávajú, že modul http bude fungovať vo svojom obvyklom správaní. Zmenou jednej z metód http efektívne prelomíme všetky ostatné http závislosti v kódovej základni.

Keďže pracujeme v dynamicky typovanom jazyku, veci nemusia zlyhať okamžite a radšej by sa predvolili nepredvídateľné správanie, ktoré robí z ladenia mimoriadne zložitú úlohu. V iných prípadoch použitia môžu existovať dve rôzne záplaty toho istého komponentu na rovnakom atribúte, v takom prípade nemôžeme skutočne predpovedať, ktorá záplata bude mať prednosť pred druhou, čo vedie k ešte nepredvídateľnejšiemu kódu.

Je tiež dôležité spomenúť, že patchovanie opíc môže mať mierne rozdiely v správaní medzi rôznymi programovacími jazykmi. Všetko závisí od jazykového dizajnu a možností implementácie. Napríklad v pythone nie všetky inštancie používajúce metódu záplaty budú ovplyvnené záplatou. Ak inštancia explicitne zavolá opravenú metódu, potom získa novú aktualizovanú verziu, naopak, ostatné inštancie, ktoré môžu mať iba atribúty ukazujúce na opravenú metódu a explicitne ju nezavolajú, získajú pôvodnú verziu, je to spôsobené tým, ako python väzba v triedach funguje.

Záver

V tomto článku sme skúmali rozdiely medzi statickými a dynamickými programovacími jazykmi na vysokej úrovni, videli sme, ako môžu dynamické programovacie jazyky ťažiť z nových paradigiem a vzorov využívajúcich prirodzenú flexibilitu, ktorú tieto jazyky ponúkajú. Príklad, ktorý sme predviedli, sa týkal Monkey patchingu, techniky používanej na rozšírenie správania kódu bez toho, aby sa zmenil zo zdroja. Videli sme prípad, kedy by použitie tejto techniky bolo prospešné spolu s jej potenciálnymi nevýhodami. Vývoj softvéru je o kompromisoch a použitie správneho riešenia problému vyžaduje od vývojára prepracované úvahy a dobré pochopenie princípov a základov architektúry.


Career Services background pattern

Kariérne služby

Contact Section background image

Ostaňme v kontakte

Code Labs Academy © 2024 Všetky práva vyhradené.