Monkey Patching v dinamičnih programskih jezikih: primer JavaScripta

Javascript
dinamično programiranje
Monkey Patching v dinamičnih programskih jezikih cover image

Uvod

Ta članek bo raziskal koncepte dinamičnih in statičnih programskih jezikov, glavne razlike med obema in kaj vsaka paradigma ponuja v smislu prednosti in pasti. To raziskovanje se bo nadalje osredotočilo na dinamične programske jezike, zlasti na enega od bistvenih vzorcev, ki jih omogoča: Monkey Patch, ta vzorec bo predstavljen s pomočjo primera v JavaScript.

Dinamični proti statičnim programskim jezikom

Terminologija

Da bi razumeli, kaj sestavlja dinamični ali statični jezik, moramo razumeti nekaj ključnih izrazov, ki se pogosto uporabljajo v tem kontekstu: Čas prevajanja, Izvajalni čas in *Preverjanje tipa *.

Prevajanje in čas izvajanja sta izraza, ki ustrezata različnim stopnjam življenjskega cikla računalniškega programa, začenši s časom prevajanja.

Čas prevajanja

Čas prevajanja je prvi korak v življenjskem ciklu programa. Razvijalec piše kodo v danem programskem jeziku. Pogosteje kot ne, stroj ne more razumeti kode, napisane v jeziku visoke ravni, zato se uporabi namenski prevajalnik, da jo prevede v vmesni format nižje ravni, ki postane pripravljen za izvajanje.

Čas izvajanja

Izvajalno okolje običajno vsebuje dva koraka: nalaganje programa v pomnilnik z dodelitvijo virov, potrebnih za njegovo izvajanje, skupaj z njegovimi navodili, in nato izvajanje programa po vrstnem redu teh navodil.

Naslednji diagram prikazuje ta postopek:

Preverjanje tipa

Preverjanje tipa je vgrajena funkcija v skoraj vseh programskih jezikih. Je zmožnost preverjanja, ali vrednost, dodeljena dani spremenljivki, ustreza pravilnemu tipu te spremenljivke. Vsak programski jezik ima drugačen način za predstavitev vrednosti dane vrste v pomnilniku. Te različne predstavitve omogočajo preverjanje ujemanja med tipom vrednosti in tipom spremenljivke, ki ji poskušate dodeliti to vrednost.

Zdaj, ko imamo visoko raven razumevanja življenjskega cikla programa in preverjanja tipa, lahko nadaljujemo z raziskovanjem statičnih programskih jezikov.

Statični programski jeziki

Statični programski jeziki, imenovani tudi statično tipizirani jeziki, so jeziki, ki uporabljajo preverjanje tipa, ki smo ga omenili v fazi prevajanja. To dejansko pomeni, da spremenljivka ohrani svoj tip iz deklaracije in ji ni mogoče dodeliti nobene druge vrednosti razen vrednosti iz njenega tipa deklaracije. Statični programski jeziki ponujajo dodatno varnost pri delu s tipi, vendar lahko v določenih primerih uporabe upočasnijo razvojni proces, ko to postane stroga omejitev.

Dinamični programski jeziki

Po drugi strani pa dinamični programski jeziki uporabljajo preverjanje tipa med izvajanjem. To pomeni, da lahko katera koli spremenljivka vsebuje katero koli vrednost na kateri koli točki v programu. To je lahko koristno, saj razvijalcu ponuja stopnjo prilagodljivosti, ki ni prisotna v statičnih jezikih. Dinamični jeziki so običajno počasnejši pri izvajanju kot njihovi statični primerki, saj vključujejo dodaten korak dinamičnega ugotavljanja tipkanja vsake spremenljivke.

Monkey Patch

Statično v primerjavi z dinamičnim tipkanjem je temeljna lastnost v programskem jeziku, uporaba ene paradigme nad drugo lahko omogoči množico različnih vzorcev in praks, ki lahko znatno izboljšajo kakovost in hitrost razvoja. Prav tako lahko odpre vrata številnim omejitvam in anti-vzorcem, če pri oblikovanju ne upoštevamo skrbnih odločitev.

Zlasti je znano, da dinamično tipizirani programski jeziki ponujajo višjo raven prilagodljivosti, saj ne omejujejo spremenljivke na eno samo vrsto. Ta prilagodljivost vključuje stroške dodatne odgovornosti razvijalca pri implementaciji in odpravljanju napak v programih, da zagotovi, da ne pride do nepredvidljivega vedenja. Vzorec opičjega našitka izhaja iz te filozofije.

Monkey Patch se nanaša na postopek razširitve/spreminjanja delovanja komponente med izvajanjem. Zadevna komponenta je lahko knjižnica, razred, metoda ali celo modul. Ideja je enaka: kos kode je narejen za izpolnitev določene naloge in cilj opičjega popravka je spremeniti ali razširiti vedenje tega dela kode, tako da opravi novo nalogo, vse brez spreminjanja kode same .

To je omogočeno v dinamičnem programskem jeziku, saj ne glede na vrsto komponente, s katero imamo opravka, ima še vedno isto strukturo objekta z različnimi atributi, atributi lahko vsebujejo metode, ki jih je mogoče prerazporediti, da se doseže novo vedenje v objektu. ne da bi se spuščali v njegovo notranjost in podrobnosti izvajanja. To postane še posebej uporabno v primeru knjižnic in modulov tretjih oseb, saj jih je težje prilagoditi.

Naslednji primer bo prikazal običajni primer uporabe, ki mu lahko koristi uporaba tehnike opičjega popravka. Javascript je bil tukaj uporabljen zaradi implementacije, vendar bi to moralo še vedno na splošno veljati za kateri koli drug dinamični programski jezik.

Primer

Implementirajte ogrodje minimalnega testiranja z izvornim HTTP modulom Node

Testiranje enot in integracije lahko spada v primere uporabe popravkov Monkey. Običajno vključujejo testne primere, ki zajemajo več kot eno storitev za testiranje integracije, ali odvisnosti API-ja in/ali baze podatkov za testiranje enote. V teh dveh scenarijih in da bi sploh dosegli cilje testiranja, želimo, da so naši testi neodvisni od teh zunanjih virov. Način za dosego tega je z norčevanjem. Norčevanje je simulacija obnašanja zunanjih storitev, tako da se lahko test osredotoči na dejansko logiko kode. Tu je lahko v pomoč opičji popravek, saj lahko spremeni metode zunanjih storitev tako, da jih nadomesti z metodami nadomestnih mest, ki jih imenujemo »škrbina«. Te metode vrnejo pričakovani rezultat v primerih testiranja, tako da se lahko izognemo sprožanju zahtev produkcijskim storitvam samo zaradi testov.

Naslednji primer je preprosta izvedba popravkov Monkey na izvornem http modulu NodeJ. Modul http je vmesnik, ki izvaja metode protokola http za NodeJ. Uporablja se predvsem za ustvarjanje barebone http strežnikov in komunikacijo z zunanjimi storitvami z uporabo http protokola.

V spodnjem primeru imamo preprost testni primer, kjer pokličemo zunanjo storitev, da pridobi seznam ID-jev uporabnikov. Namesto da pokličemo dejansko storitev, popravimo metodo http get, tako da le vrne pričakovani rezultat, ki je niz naključnih ID-jev uporabnikov. To se morda ne zdi zelo pomembno, saj le pridobivamo podatke, vendar če izvedemo drug preskusni primer, ki vključuje spreminjanje neke vrste podatkov, lahko pomotoma spremenimo podatke o proizvodnji med izvajanjem testov.

Tako lahko implementiramo naše funkcionalnosti in pišemo teste za vsako funkcionalnost, hkrati pa zagotavljamo varnost naših proizvodnih storitev.

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

Zgornja koda je preprosta, uvozimo modul http, znova dodelimo metodo http.get z novo metodo, ki samo vrne niz ID-jev. Zdaj pokličemo novo zakrpano metodo znotraj testnega primera in dobimo nov pričakovan rezultat.

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

Pogoste pasti in omejitve

Ne bi smelo biti presenečenje, da ima opičji popravek svoje napake in omejitve. V kontekstu modulov v sistemu modulov vozlišča se popravki globalnega modula, kot je http, štejejo za operacijo s stranskimi učinki, to pa zato, ker je http dostopen s katere koli točke znotraj kodne baze in je katera koli druga entiteta lahko odvisna od njega. Te entitete pričakujejo, da bo modul http deloval kot običajno, s spremembo ene od metod http učinkovito prekinemo vse druge odvisnosti http znotraj kodne baze.

Ker delujemo v dinamično tipkanem jeziku, stvari morda ne bodo odpovedale takoj in bi raje privzeto uporabili nepredvidljivo vedenje, zaradi česar je odpravljanje napak izjemno zapletena naloga. V drugih primerih uporabe lahko obstajata dva različna popravka iste komponente na istem atributu, v tem primeru ne moremo zares predvideti, kateri popravek bo imel prednost pred drugim, kar bo povzročilo še bolj nepredvidljivo kodo.

Pomembno je tudi omeniti, da ima opičji popravek lahko manjše razlike v obnašanju med različnimi programskimi jeziki. Vse je odvisno od jezikovne zasnove in izbir izvedbe. Na primer, v pythonu popravek ne bo vplival na vse primerke, ki uporabljajo popravljeno metodo. Če primerek izrecno pokliče popravljeno metodo, bo dobil novo posodobljeno različico, nasprotno pa bodo drugi primerki, ki imajo morda samo atribute, ki kažejo na popravljeno metodo in je ne izrecno kličejo, prejeli izvirno različico, to je zaradi tega, kako python vezava v razredih deluje.

Zaključek

V tem članku smo raziskali razlike na visoki ravni med statičnimi in dinamičnimi programskimi jeziki ter videli, kako lahko dinamični programski jeziki izkoristijo nove paradigme in vzorce, ki izkoriščajo inherentno prilagodljivost, ki jo ponujajo ti jeziki. Primer, ki smo ga predstavili, je bil povezan z opičjim popravkom, tehniko, ki se uporablja za razširitev obnašanja kode, ne da bi jo spremenili iz vira. Videli smo primer, ko bi bila uporaba te tehnike koristna skupaj z njenimi morebitnimi pomanjkljivostmi. Pri razvoju programske opreme gre predvsem za kompromise in uporaba prave rešitve za težavo zahteva natančno premisleke razvijalca ter dobro razumevanje principov in temeljev arhitekture.


Career Services background pattern

Karierne storitve

Contact Section background image

Ostanimo v stiku

Code Labs Academy © 2024 Vse pravice pridržane.