Opičí záplatování v dynamických programovacích jazycích: příklad JavaScriptu

Javascript
dynamické programování
Opičí patchování v dynamických programovacích jazycích cover image

Úvod

Tento článek prozkoumá koncepty dynamických a statických programovacích jazyků, hlavní rozdíly mezi nimi a to, co každé paradigma poskytuje z hlediska výhod a úskalí. Tento průzkum se dále zaměří na dynamické programovací jazyky, zejména na jeden ze základních vzorů, které umožňuje: Monkey Patch, tento vzor bude předveden na příkladu v JavaScriptu.

Dynamické vs statické programovací jazyky

Terminologie

Abychom porozuměli tomu, co představuje dynamický nebo statický jazyk, musíme pochopit několik klíčových termínů běžně používaných v tomto kontextu: Čas kompilace, Runtime a *Kontrola typu *.

Kompilace a Runtime jsou dva pojmy, které odpovídají různým fázím životního cyklu počítačového programu, počínaje dobou kompilace.

Doba kompilace

Doba kompilace je prvním krokem v životním cyklu programu. Vývojář píše kód v daném programovacím jazyce. Více často než ne, stroj není schopen porozumět kódu napsanému v jazyce na vysoké úrovni, takže se používá vyhrazený kompilátor, který jej přeloží do nižšího středního formátu, který je připraven k provedení.

Doba běhu

Runtime obvykle zahrnuje dva kroky: načtení programu do paměti alokací zdrojů potřebných k jeho provedení spolu s jeho instrukcemi a následné spuštění programu podle pořadí těchto instrukcí.

Následující diagram ilustruje tento proces:

Kontrola typu

Kontrola typu je vestavěná funkce téměř ve všech programovacích jazycích. Je to schopnost zkontrolovat, zda hodnota přiřazená dané proměnné odpovídá správnému typu této proměnné. Každý programovací jazyk má jiný způsob reprezentace hodnoty daného typu v paměti. Tyto různé reprezentace umožňují zkontrolovat shodu mezi typem hodnoty a typem proměnné, které se pokoušíte přiřadit tuto hodnotu.

Nyní, když na vysoké úrovni rozumíme životnímu cyklu programu a kontrole typu, můžeme přistoupit ke zkoumání statických programovacích jazyků.

Statické programovací jazyky

Statické programovací jazyky, označované také jako staticky typované jazyky, jsou jazyky, které aplikují kontrolu typu, kterou jsme zmínili ve fázi kompilace. To v podstatě znamená, že proměnná si ponechává svůj typ z deklarace a nelze k ní přiřadit žádnou jinou hodnotu než hodnoty z jejího typu deklarace. Statické programovací jazyky nabízejí extra bezpečnost při práci s typy, ale mohou zpomalit proces vývoje v určitých případech použití, pokud se to stane tvrdým omezením.

Dynamické programovací jazyky

Dynamické programovací jazyky na druhé straně používají kontrolu typu za běhu. To znamená, že jakákoli proměnná může mít libovolnou hodnotu v libovolném bodě programu. To může být přínosné, protože nabízí úroveň flexibility pro vývojáře, která není přítomna ve statických jazycích. Dynamické jazyky mají tendenci být pomalejší při provádění než jejich statické protějšky, protože zahrnují další krok dynamického zjišťování typu každé proměnné.

Opičí náplast

Statické vs dynamické typování je základním rysem programovacího jazyka, přecházení jednoho paradigmatu přes druhé může umožnit řadu různých vzorů a postupů, které mohou výrazně zlepšit kvalitu a rychlost vývoje. Může také otevřít dveře mnoha omezením a protivzorům, pokud při rozhodování o návrhu nejsou věnována žádná pečlivá hlediska.

Zejména je známo, že dynamicky typované programovací jazyky nabízejí vyšší úroveň flexibility, protože neomezují proměnnou na jeden typ. Tato flexibilita je spojena s náklady na další odpovědnost vývojáře při implementaci a ladění programů, aby se zajistilo, že nedochází k nepředvídatelnému chování. Vzor opičí nášivky pochází z této filozofie.

Monkey Patch odkazuje na proces rozšíření/změny fungování komponenty za běhu. Dotyčnou komponentou může být knihovna, třída, metoda nebo dokonce modul. Myšlenka je stejná: kus kódu je vytvořen pro splnění určitého úkolu a cílem opičího záplatování je změnit nebo rozšířit chování tohoto kusu kódu tak, aby splnil nový úkol, to vše bez změny samotného kódu. .

To je možné v dynamickém programovacím jazyce, protože bez ohledu na to, s jakým typem komponenty máme co do činění, má stále stejnou strukturu objektu s různými atributy, atributy mohou obsahovat metody, které lze přeřadit, aby se dosáhlo nového chování v objektu. aniž bychom zacházeli do jeho vnitřností a detailů implementace. To se stává zvláště užitečné v případě knihoven a modulů třetích stran, protože ty bývá obtížnější vyladit.

Následující příklad předvede případ běžného použití, který může těžit z použití techniky opičí opravy. Za účelem implementace zde byl použit Javascript, ale to by se mělo obecně vztahovat na jakýkoli jiný dynamický programovací jazyk.

Příklad

Implementujte minimální testovací rámec s nativním modulem HTTP Node

Testování jednotek a integrace může spadat pod případy použití opravy Monkey. Obvykle zahrnují testovací případy, které zahrnují více než jednu službu pro testování integrace nebo závislosti API a/nebo databáze pro testování jednotek. V těchto dvou scénářích a za účelem dosažení cílů testování v první řadě bychom chtěli, aby naše testy byly nezávislé na těchto externích zdrojích. Způsob, jak toho dosáhnout, je zesměšňovat. Mocking simuluje chování externích služeb, takže se test může zaměřit na skutečnou logiku kódu. Opičí záplatování zde může být užitečné, protože může upravit metody externích služeb tím, že je nahradí zástupnými metodami, které nazýváme „stub“. Tyto metody vracejí očekávaný výsledek v testovacích případech, takže se můžeme vyhnout iniciování požadavků na produkční služby jen kvůli testům.

Následující příklad je jednoduchá implementace opravy Monkey na nativním http modulu NodeJs. Modul http je rozhraní, které implementuje metody protokolu http pro NodeJs. Používá se především k vytváření barebone http serverů a komunikaci s externími službami pomocí protokolu http.

V níže uvedeném příkladu máme jednoduchý testovací případ, kdy zavoláme externí službu, abychom načetli seznam ID uživatelů. Namísto volání skutečné služby opravíme metodu http get, takže pouze vrátí očekávaný výsledek, což je pole náhodných uživatelských ID. To se nemusí zdát příliš důležité, protože pouze načítáme data, ale pokud implementujeme další testovací případ, který zahrnuje změnu dat určitého druhu, mohli bychom při spuštění testů náhodně změnit data o produkci.

Tímto způsobem můžeme implementovat naše funkce a psát testy pro každou funkci a zároveň zajistit bezpečnost našich produkčních služeb.

// 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");
});

Výše uvedený kód je jednoduchý, importujeme modul http, znovu přiřadíme metodu http.get novou metodou, která pouze vrací pole id. Nyní zavoláme novou opravenou metodu uvnitř testovacího případu a dostaneme nový očekávaný výsledek.

~/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.

Běžná úskalí a omezení

Nemělo by být žádným překvapením, že opičí patchování má své vlastní chyby a omezení. V kontextu modulů v systému modulů uzlů je záplatování globálního modulu, jako je http, považováno za operaci s vedlejšími účinky, je to proto, že http je přístupné z jakéhokoli místa uvnitř kódové základny a jakákoli jiná entita na něm může být závislá. Tyto entity očekávají, že modul http bude fungovat ve svém obvyklém chování, změnou jedné z metod http účinně narušíme všechny ostatní http závislosti v kódové základně.

Vzhledem k tomu, že pracujeme v dynamicky typovaném jazyce, věci nemusí selhat okamžitě a raději by se nastavilo nepředvídatelné chování, které činí ladění extrémně složitým úkolem. V jiných případech použití mohou existovat dvě různé záplaty stejné komponenty na stejném atributu, v takovém případě nemůžeme skutečně předpovědět, která záplata bude mít přednost před druhou, což povede k ještě nepředvídatelnějšímu kódu.

Je také důležité zmínit, že opičí patchování může mít mezi různými programovacími jazyky drobné odchylky v chování. Vše závisí na jazykovém designu a volbě implementace. Například v pythonu nebudou záplatou ovlivněny všechny instance používající záplatovanou metodu. Pokud instance explicitně zavolá opravenou metodu, získá novou aktualizovanou verzi, naopak ostatní instance, které mohou mít pouze atributy ukazující na opravenou metodu a explicitně ji nevolají, získají původní verzi, je to způsobeno tím, jak python vazba ve třídách funguje.

Závěr

V tomto článku jsme prozkoumali rozdíly na vysoké úrovni mezi statickými a dynamickými programovacími jazyky, viděli jsme, jak mohou dynamické programovací jazyky těžit z nových paradigmat a vzorů využívajících přirozenou flexibilitu, kterou tyto jazyky nabízejí. Příklad, který jsme předvedli, se týkal Monkey patchingu, techniky používané k rozšíření chování kódu bez jeho změny ze zdroje. Viděli jsme případ, kdy by použití této techniky bylo přínosné spolu s jejími potenciálními nevýhodami. Vývoj softwaru je o kompromisech a použití správného řešení problému vyžaduje propracované úvahy od vývojáře a dobré pochopení principů a základů architektury.


Career Services background pattern

Kariérní služby

Contact Section background image

Zůstaňme v kontaktu

Code Labs Academy © 2024 Všechna práva vyhrazena.