Monkey Patching i dynamiske programmeringssprog: Et JavaScript-eksempel

Javascript
dynamisk programmering
Monkey Patching i dynamiske programmeringssprog cover image

Introduktion

Denne artikel vil udforske begreberne dynamiske og statiske programmeringssprog, de vigtigste forskelle mellem de to, og hvad hvert paradigme giver i form af fordele og faldgruber. Denne udforskning vil yderligere fokusere på dynamiske programmeringssprog, især et af de væsentlige mønstre, det muliggør: Monkey Patch, dette mønster vil blive fremvist ved hjælp af et eksempel i JavaScript.

Dynamiske vs statiske programmeringssprog

Terminologi

For at forstå, hvad der udgør et dynamisk sprog eller et statisk sprog, er vi nødt til at etablere en forståelse af nogle få nøglebegreber, der almindeligvis bruges i denne sammenhæng: Kompileringstid, Runtime og *Typekontrol *.

Kompilering og Runtime er to udtryk, der svarer til forskellige stadier i et computerprograms livscyklus, startende med kompileringstid.

Kompileringstid

Kompileringstiden er det første trin i et programs livscyklus. En udvikler skriver kode i et givet programmeringssprog. Oftere end ikke er maskinen ude af stand til at forstå koden skrevet på et højt niveau sprog, så en dedikeret compiler bruges til at oversætte den til et lavere niveau mellemformat, der bliver klar til udførelse.

Runtime

Runtime indkapsler normalt to trin: indlæsning af programmet i hukommelsen ved at allokere de nødvendige ressourcer til dets udførelse sammen med dets instruktioner, og derefter udføre programmet efter rækkefølgen af ​​disse instruktioner.

Følgende diagram illustrerer denne proces:

Typekontrol

Typekontrol er en indbygget funktion i næsten alle programmeringssprog. Det er evnen til at kontrollere, om en værdi, der er tildelt en given variabel, svarer til den korrekte type af denne variabel. Hvert programmeringssprog har en forskellig måde at repræsentere en værdi af en given type i hukommelsen. Disse forskellige repræsentationer gør det muligt at kontrollere overensstemmelsen mellem typen af ​​en værdi og typen af ​​en variabel, du forsøger at tildele værdien til.

Nu hvor vi har en forståelse på højt niveau af et programs livscyklus og typekontrol, kan vi fortsætte med at udforske statiske programmeringssprog.

Statiske programmeringssprog

Statiske programmeringssprog, også kaldet statisk indtastede sprog, er sprog, der anvender den typekontrol, vi nævnte i kompileringsfasen. Dette betyder i praksis, at en variabel holder sin type fra erklæring, og der kan ikke tildeles nogen værdi til den udover værdier fra dens erklæringstype. Statiske programmeringssprog giver ekstra sikkerhed, når de håndterer typer, men kan bremse udviklingsprocessen i visse anvendelsestilfælde, når dette bliver en hård begrænsning.

Dynamiske programmeringssprog

Dynamiske programmeringssprog anvender på den anden side typekontrol under kørsel. Det betyder, at enhver variabel kan indeholde en hvilken som helst værdi på et hvilket som helst tidspunkt i programmet. Dette kan være fordelagtigt, da det giver udvikleren et niveau af fleksibilitet, som ikke er til stede i de statiske sprog. Dynamiske sprog har en tendens til at være langsommere ved udførelse end deres statiske modstykker, da de involverer et ekstra trin med dynamisk at finde ud af indtastningen af ​​hver variabel.

Monkey Patch

Statisk vs dynamisk typing er en grundlæggende egenskab i et programmeringssprog, at gå med det ene paradigme frem for det andet kan muliggøre et væld af forskellige mønstre og praksisser, der væsentligt kan forbedre kvaliteten og udviklingshastigheden. Det kan også åbne døren for mange begrænsninger og anti-mønstre, hvis der ikke tages nøje overvejelser, når der træffes designbeslutninger.

Især er dynamisk indtastede programmeringssprog kendt for at tilbyde et højere niveau af fleksibilitet, da de ikke begrænser en variabel til en enkelt type. Denne fleksibilitet kommer med omkostningerne til yderligere ansvar for udvikleren ved implementering og fejlretning af programmer for at sikre, at der ikke opstår uforudsigelig adfærd. Abemærkemønsteret kommer fra denne filosofi.

Monkey Patch refererer til processen med at udvide/ændre en komponents funktion under kørsel. Den pågældende komponent kan være et bibliotek, en klasse, en metode eller endda et modul. Ideen er den samme: et stykke kode er lavet til at udføre en bestemt opgave, og målet med monkey patching er at ændre eller udvide adfærden for det stykke kode, så det udfører en ny opgave, alt sammen uden at ændre selve koden .

Dette er gjort muligt i dynamisk programmeringssprog, da uanset hvilken type komponent vi har at gøre med, har det stadig den samme struktur som et objekt med forskellige attributter, attributterne kan indeholde metoder, der kan omtildeles for at opnå en ny adfærd i objektet uden at gå ind i dets interne forhold og detaljer om implementering. Dette bliver især nyttigt i tilfælde af tredjepartsbiblioteker og -moduler, da disse har tendens til at være sværere at justere.

Følgende eksempel viser en almindelig anvendelse, der kan drage fordel af at bruge abeplasterteknikken. Javascript blev brugt for implementeringens skyld her, men dette burde stadig gælde for ethvert andet dynamisk programmeringssprog.

Eksempel

Implementer en minimal testramme med Nodes Native HTTP-modul

Enheds- og integrationstestning kan falde ind under anvendelsestilfælde af Monkey-patching. De involverer normalt testcases, der spænder over mere end én tjeneste til integrationstest, eller API- og/eller databaseafhængigheder til enhedstest. I disse to scenarier, og for at nå målene med test i første omgang, ønsker vi, at vores tests er uafhængige af disse eksterne ressourcer. Måden at opnå dette på er gennem hån. Mocking simulerer opførsel af eksterne tjenester, så testen kan fokusere på kodens faktiske logik. Monkey patching kan være nyttigt her, da det kan ændre metoderne for de eksterne tjenester ved at erstatte dem med pladsholdermetoder, som vi kalder "stub". Disse metoder returnerer det forventede resultat i testcaserne, så vi kan undgå at igangsætte forespørgsler til produktionstjenester blot for tests skyld.

Følgende eksempel er en simpel implementering af Monkey-patching på NodeJs native http-modul. http-modulet er grænsefladen, der implementerer http-protokolmetoderne for NodeJ'er. Det bruges hovedsageligt til at oprette barebone http-servere og kommunikere med eksterne tjenester ved hjælp af http-protokollen.

I eksemplet nedenfor har vi et simpelt testtilfælde, hvor vi ringer til en ekstern tjeneste for at hente listen over bruger-id'er. I stedet for at kalde den faktiske tjeneste, patcher vi http get-metoden, så den bare returnerer det forventede resultat, som er en række tilfældige bruger-id'er. Dette ser måske ikke ud til at være af stor betydning, da vi bare henter data, men hvis vi implementerer en anden testcase, der involverer ændring af data af en eller anden art, kan vi ved et uheld ændre data om produktionen, når vi kører tests.

På denne måde kan vi implementere vores funktionaliteter og skrive test for hver funktionalitet, samtidig med at vi sikrer sikkerheden af ​​vores produktionstjenester.

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

Ovenstående kode er ligetil, vi importerer http-modulet, gentildeler http.get-metoden med en ny metode, der bare returnerer en række id'er. Nu kalder vi den nye patchede metode inde i testcasen, og vi får det nye forventede resultat.

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

Almindelige faldgruber og begrænsninger

Det burde ikke komme som nogen overraskelse, at abe-patching har sine egne fejl og begrænsninger. I forbindelse med moduler i nodemodulsystemet betragtes patchning af et globalt modul såsom http som en operation med bivirkninger, det skyldes, at http er tilgængelig fra ethvert punkt inde i kodebasen, og enhver anden enhed kan have en afhængighed af den. Disse entiteter forventer, at http-modulet fungerer i sin sædvanlige adfærd, ved at ændre en af ​​http-metoderne bryder vi effektivt alle andre http-afhængigheder inde i kodebasen.

Da vi opererer i et dynamisk indtastet sprog, fejler tingene måske ikke med det samme og vil snarere standard til en uforudsigelig adfærd, som gør fejlfinding til en ekstremt kompleks opgave. I andre tilfælde kan der være to forskellige patches af den samme komponent på den samme attribut, i hvilket tilfælde vi ikke rigtig kan forudsige, hvilken patch der vil have forrang frem for den anden, hvilket resulterer i en endnu mere uforudsigelig kode.

Det er også vigtigt at nævne, at monkey patching kan have små variationer i adfærd mellem forskellige programmeringssprog. Det hele afhænger af sprogdesignet og valg af implementering. For eksempel i python vil ikke alle forekomster, der bruger en patched-metode, blive påvirket af patchen. Hvis en instans eksplicit kalder den patchede metode, vil den få den nye opdaterede version, tværtimod, andre instanser, der måske kun har attributter, der peger på den patchede metode og ikke eksplicit kalder den, vil få den originale version, dette skyldes hvordan python bindende i klasser fungerer.

Konklusion

I denne artikel undersøgte vi forskellene på højt niveau mellem statiske og dynamiske programmeringssprog, vi så, hvordan dynamiske programmeringssprog kan drage fordel af nye paradigmer og mønstre, der udnytter den iboende fleksibilitet, disse sprog tilbyder. Eksemplet, vi fremviste, var relateret til Monkey-patching, en teknik, der bruges til at udvide kodens adfærd uden at ændre den fra kilden. Vi så et tilfælde, hvor brugen af ​​denne teknik ville være gavnlig sammen med dens potentielle ulemper. Softwareudvikling handler om afvejninger, og at anvende den rigtige løsning til problemet kræver omfattende overvejelser fra udvikleren og en god forståelse af arkitekturens principper og grundlæggende principper.


Career Services background pattern

Karriereservice

Contact Section background image

Lad os holde kontakten

Code Labs Academy © 2024 Alle rettigheder forbeholdes.