Monkey Patching i dynamiske programmeringsspråk: Et JavaScript-eksempel

Javascript
dynamisk programmering
Monkey Patching i dynamiske programmeringsspråk cover image

Introduksjon

Denne artikkelen vil utforske konseptene til dynamiske og statiske programmeringsspråk, de viktigste forskjellene mellom de to, og hva hvert paradigme gir når det gjelder fordeler og fallgruver. Denne utforskningen vil videre fokusere på dynamiske programmeringsspråk, spesielt et av de essensielle mønstrene den muliggjør: Monkey Patch, dette mønsteret vil bli vist frem ved hjelp av et eksempel i JavaScript.

Dynamiske vs statiske programmeringsspråk

Terminologi

For å forstå hva som utgjør et dynamisk språk eller et statisk språk, må vi etablere en forståelse av noen nøkkelbegreper som vanligvis brukes i denne sammenhengen: Kompileringstid, Runtime og *Typekontroll *.

Kompilere og kjøretid er to termer som tilsvarer forskjellige stadier i livssyklusen til et dataprogram, og starter med kompileringstid.

Kompileringstid

Kompileringstid er det første trinnet i livssyklusen til et program. En utvikler skriver kode i et gitt programmeringsspråk. Oftere enn ikke er maskinen ikke i stand til å forstå koden skrevet på et høynivåspråk, så en dedikert kompilator brukes til å oversette den til et mellomformat på lavere nivå som blir klart for kjøring.

Kjøretid

Runtime innkapsler vanligvis to trinn: laste programmet inn i minnet ved å tildele ressursene som trengs for utførelse sammen med instruksjonene, og deretter kjøre programmet etter rekkefølgen til disse instruksjonene.

Følgende diagram illustrerer denne prosessen:

Typekontroll

Typekontroll er en innebygd funksjon i nesten alle programmeringsspråk. Det er muligheten til å sjekke om en verdi tilordnet en gitt variabel samsvarer med den riktige typen av den variabelen. Hvert programmeringsspråk har en annen måte å representere en verdi av en gitt type i minnet. Disse forskjellige representasjonene gjør det mulig å sjekke samsvaret mellom typen av en verdi og typen av en variabel du prøver å tilordne den verdien til.

Nå som vi har en høy-nivå forståelse av et programs livssyklus og typekontroll, kan vi fortsette å utforske statiske programmeringsspråk.

Statiske programmeringsspråk

Statiske programmeringsspråk, også referert til som statisk skrevet språk, er språk som bruker typekontrollen vi nevnte i kompileringsfasen. Dette betyr i praksis at en variabel holder sin type fra deklarasjon og ingen verdi kan tildeles den annet enn verdier fra dens deklarasjonstype. Statiske programmeringsspråk gir ekstra sikkerhet når de arbeider med typer, men kan bremse utviklingsprosessen i visse brukstilfeller når dette blir en tøff restriksjon.

Dynamiske programmeringsspråk

Dynamiske programmeringsspråk, derimot, bruker typekontroll under kjøretid. Dette betyr at enhver variabel kan inneholde hvilken som helst verdi når som helst i programmet. Dette kan være fordelaktig siden det gir en grad av fleksibilitet til utvikleren som ikke er tilstede i de statiske språkene. Dynamiske språk har en tendens til å være tregere ved utførelse enn deres statiske motstykker siden de involverer et ekstra trinn med dynamisk å finne ut hvordan hver variabel skrives.

Monkey Patch

Statisk vs dynamisk typing er en grunnleggende egenskap i et programmeringsspråk, å gå med ett paradigme over det andre kan muliggjøre en rekke forskjellige mønstre og praksiser som kan forbedre kvaliteten og utviklingshastigheten betydelig. Det kan også åpne døren for mange begrensninger og anti-mønstre hvis det ikke tas nøye hensyn når du tar designbeslutninger.

Spesielt er dynamisk skrevet programmeringsspråk kjent for å tilby et høyere nivå av fleksibilitet siden de ikke begrenser en variabel til en enkelt type. Denne fleksibiliteten kommer med kostnadene for ekstra ansvar for utvikleren når de implementerer og feilsøker programmer for å sikre at ingen uforutsigbar atferd oppstår. Apelappemønsteret kommer fra denne filosofien.

Monkey Patch refererer til prosessen med å utvide/endre funksjonene til en komponent under kjøring. Den aktuelle komponenten kan være et bibliotek, en klasse, en metode eller til og med en modul. Ideen er den samme: en kodebit er laget for å utføre en bestemt oppgave, og målet med apepatching er å endre eller utvide oppførselen til den kodebiten slik at den utfører en ny oppgave, alt uten å endre selve koden .

Dette er gjort mulig i dynamisk programmeringsspråk siden uansett hvilken type komponent vi har å gjøre med, har den fortsatt samme struktur som et objekt med forskjellige attributter, attributtene kan inneholde metoder som kan omdisponeres for å oppnå en ny atferd i objektet uten å gå inn på dets indre og detaljer om implementering. Dette blir spesielt nyttig i tilfelle tredjeparts biblioteker og moduler, da de har en tendens til å være vanskeligere å justere.

Følgende eksempel vil vise frem en vanlig brukssak som kan dra nytte av å bruke monkey patch-teknikken. Javascript ble brukt for implementeringen her, men dette bør fortsatt gjelde for alle andre dynamiske programmeringsspråk.

Eksempel

Implementer et minimalt testrammeverk med Nodes Native HTTP-modul

Enhets- og integrasjonstesting kan falle inn under brukstilfellene av Monkey-patching. De involverer vanligvis testsaker som spenner over mer enn én tjeneste for integrasjonstesting, eller API- og/eller databaseavhengigheter for enhetstesting. I disse to scenariene, og for å oppnå målene med testing i utgangspunktet, ønsker vi at testene våre skal være uavhengige av disse eksterne ressursene. Måten å oppnå dette på er gjennom hån. Mocking simulerer oppførselen til eksterne tjenester slik at testen kan fokusere på den faktiske logikken til koden. Monkey-patching kan være nyttig her siden det kan endre metodene til de eksterne tjenestene ved å erstatte dem med plassholdermetoder som vi kaller "stub". Disse metodene returnerer det forventede resultatet i testsakene, slik at vi kan unngå å starte forespørsler til produksjonstjenester bare for testenes skyld.

Følgende eksempel er en enkel implementering av Monkey-patching på NodeJs opprinnelige http-modul. http-modulen er grensesnittet som implementerer http-protokollmetodene for NodeJs. Den brukes hovedsakelig til å lage barebone http-servere og kommunisere med eksterne tjenester ved hjelp av http-protokollen.

I eksemplet nedenfor har vi et enkelt testtilfelle der vi ringer en ekstern tjeneste for å hente listen over bruker-IDer. I stedet for å ringe den faktiske tjenesten, lapper vi http get-metoden slik at den bare returnerer det forventede resultatet som er en rekke tilfeldige bruker-IDer. Dette virker kanskje ikke av stor betydning siden vi bare henter data, men hvis vi implementerer et annet testtilfelle som involverer endring av data av noe slag, kan vi ved et uhell endre data på produksjonen når tester kjøres.

På denne måten kan vi implementere funksjonene våre, og skrive tester for hver funksjonalitet samtidig som vi sikrer sikkerheten til våre produksjonstjenester.

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

Koden ovenfor er enkel, vi importerer http-modulen, tilordner http.get-metoden på nytt med en ny metode som bare returnerer en rekke id-er. Nå kaller vi den nye patchede metoden inne i testsaken og vi får det nye forventede resultatet.

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

Vanlige fallgruver og begrensninger

Det burde ikke komme som noen overraskelse at apepatching har sine egne feil og begrensninger. I sammenheng med moduler i nodemodulsystemet, regnes patching av en global modul som http som en operasjon med bivirkninger, dette er fordi http er tilgjengelig fra et hvilket som helst punkt inne i kodebasen og enhver annen enhet kan ha en avhengighet av den. Disse enhetene forventer at http-modulen fungerer i sin vanlige oppførsel, ved å endre en av http-metodene bryter vi effektivt alle andre http-avhengigheter inne i kodebasen.

Siden vi opererer i et dynamisk skrevet språk, kan det hende at ting ikke svikter umiddelbart, og at vi heller vil gå tilbake til en uforutsigbar oppførsel som gjør feilsøking til en ekstremt kompleks oppgave. I andre brukstilfeller kan det være to forskjellige patcher av samme komponent på samme attributt, i så fall kan vi ikke forutsi hvilken patch som vil ha forrang over den andre, noe som resulterer i en enda mer uforutsigbar kode.

Det er også viktig å nevne at monkey patching kan ha små variasjoner i oppførsel mellom ulike programmeringsspråk. Alt avhenger av språkdesign og valg av implementering. For eksempel, i python vil ikke alle forekomster som bruker en patch-metode bli påvirket av patchen. Hvis en forekomst eksplisitt kaller den patchede metoden, vil den få den nye oppdaterte versjonen, tvert imot, andre forekomster som kanskje bare har attributter som peker til den patchede metoden og ikke eksplisitt kaller den, vil få den opprinnelige versjonen, dette er på grunn av hvordan python binding i klasser opererer.

Konklusjon

I denne artikkelen utforsket vi forskjellene på høyt nivå mellom statiske og dynamiske programmeringsspråk, vi så hvordan dynamiske programmeringsspråk kan dra nytte av nye paradigmer og mønstre som utnytter den iboende fleksibiliteten disse språkene tilbyr. Eksemplet vi viste frem var relatert til Monkey-patching, en teknikk som brukes til å utvide oppførselen til kode uten å endre den fra kilden. Vi så et tilfelle der bruken av denne teknikken ville være fordelaktig sammen med dens potensielle ulemper. Programvareutvikling handler om avveininger, og å bruke den riktige løsningen for problemet krever forseggjorte vurderinger fra utvikleren og en god forståelse av arkitekturprinsipper og grunnleggende.


Career Services background pattern

Karrieretjenester

Contact Section background image

La oss holde kontakten

Code Labs Academy © 2025 Alle rettigheter forbeholdes.