Monkey Patching en llenguatges de programació dinàmica: un exemple de JavaScript

Javascript
programació dinàmica
Monkey Patching en llenguatges de programació dinàmica cover image

Introducció

En aquest article s'explorarà els conceptes de llenguatges de programació dinàmics i estàtics, les principals diferències entre ambdós i què aporta cada paradigma en termes d'avantatges i inconvenients. Aquesta exploració se centrarà encara més en els llenguatges de programació dinàmics, especialment en un dels patrons essencials que permet: Monkey Patch, aquest patró es mostrarà amb l'ajuda d'un exemple a JavaScript.

Llenguatges de programació dinàmics i estàtics

Terminologia

Per entendre què és un llenguatge dinàmic o estàtic, hem d'establir una comprensió d'alguns termes clau que s'utilitzen habitualment en aquest context: Temps de compilació, Temps d'execució i *Comprovació de tipus *.

Compile i Runtime són dos termes que corresponen a diferents etapes del cicle de vida d'un programa informàtic, començant pel temps de compilació.

Temps de compilació

El temps de compilació és el primer pas en el cicle de vida d'un programa. Un desenvolupador escriu codi en un llenguatge de programació determinat. La majoria de les vegades, la màquina no pot entendre el codi escrit en un llenguatge d'alt nivell, de manera que s'utilitza un compilador dedicat per traduir-lo a un format intermedi de nivell inferior que està preparat per a l'execució.

Temps d'execució

El temps d'execució sol encapsular dos passos: carregar el programa a la memòria assignant els recursos necessaris per a la seva execució juntament amb les seves instruccions, i després executar el programa seguint l'ordre d'aquestes instruccions.

El diagrama següent il·lustra aquest procés:

Comprovació de tipus

La comprovació de tipus és una característica integrada en gairebé tots els llenguatges de programació. És la capacitat de comprovar si un valor assignat a una determinada variable correspon al tipus correcte d'aquesta variable. Cada llenguatge de programació té una manera diferent de representar un valor d'un tipus determinat a la memòria. Aquestes diferents representacions permeten comprovar la correspondència entre el tipus d'un valor i el tipus de variable a la qual intenteu assignar aquest valor.

Ara que tenim una comprensió d'alt nivell del cicle de vida d'un programa i la comprovació de tipus, podem procedir a explorar els llenguatges de programació estàtics.

Llenguatges de programació estàtics

Els llenguatges de programació estàtica, també coneguts com a llenguatges de tipografia estàtica, són llenguatges que apliquen la verificació de tipus que hem esmentat a la fase de compilació. Això significa efectivament que una variable manté el seu tipus de declaració i no se li pot assignar cap valor més que els valors del seu tipus de declaració. Els llenguatges de programació estàtics ofereixen una seguretat addicional quan es tracten amb tipus, però poden alentir el procés de desenvolupament en determinats casos d'ús quan això es converteix en una restricció severa.

Llenguatges de programació dinàmica

Els llenguatges de programació dinàmica, d'altra banda, apliquen la verificació de tipus en temps d'execució. Això significa que qualsevol variable pot contenir qualsevol valor en qualsevol punt del programa. Això pot ser beneficiós ja que ofereix un nivell de flexibilitat al desenvolupador que no està present en els llenguatges estàtics. Els llenguatges dinàmics solen ser més lents en l'execució que els seus homòlegs estàtics, ja que impliquen un pas addicional per esbrinar dinàmicament l'escriptura de cada variable.

Pegat del mico

La tipificació estàtica vs dinàmica és un tret fonamental en un llenguatge de programació, anar amb un paradigma sobre l'altre pot permetre una sèrie de patrons i pràctiques diferents que poden millorar significativament la qualitat i la velocitat del desenvolupament. També pot obrir la porta a moltes limitacions i anti-patrons si no es fan consideracions acurades a l'hora de prendre decisions de disseny.

En particular, se sap que els llenguatges de programació de tipus dinàmic ofereixen un nivell més alt de flexibilitat, ja que no restringeixen una variable a un sol tipus. Aquesta flexibilitat comporta el cost d'una responsabilitat addicional per al desenvolupador a l'hora d'implementar i depurar programes per assegurar-se que no es produeixin comportaments impredictibles. El patró de pegat de mico prové d'aquesta filosofia.

Monkey Patch es refereix al procés d'estendre/canviar el funcionament d'un component en temps d'execució. El component en qüestió pot ser una biblioteca, una classe, un mètode o fins i tot un mòdul. La idea és la mateixa: es fa un tros de codi per dur a terme una tasca determinada, i l'objectiu del pedaç de mico és canviar o ampliar el comportament d'aquesta peça de codi perquè faci una tasca nova, tot sense canviar el codi en si. .

Això és possible en llenguatge de programació dinàmica, ja que independentment del tipus de component que estem tractant, encara té la mateixa estructura d'un objecte amb diferents atributs, els atributs poden contenir mètodes que es poden reassignar per aconseguir un nou comportament en l'objecte. sense entrar en els seus detalls interns i d'implementació. Això esdevé especialment útil en el cas de biblioteques i mòduls de tercers, ja que solen ser més difícils de modificar.

L'exemple següent mostrarà un cas d'ús comú que es pot beneficiar de l'ús de la tècnica del pegat del mico. Javascript es va utilitzar per a la implementació aquí, però això encara s'hauria d'aplicar a qualsevol altre llenguatge de programació dinàmic.

Exemple

Implementeu un marc de proves mínim amb el mòdul HTTP natiu de Node

Les proves d'unitat i d'integració poden incloure els casos d'ús del pegat de Monkey. Normalment impliquen casos de prova que abasten més d'un servei per a proves d'integració o dependències d'API i/o bases de dades per a proves unitàries. En aquests dos escenaris, i per assolir els objectius de les proves en primer lloc, voldríem que les nostres proves fossin independents d'aquests recursos externs. La manera d'aconseguir-ho és a través de la burla. La burla és simular el comportament dels serveis externs perquè la prova es pugui centrar en la lògica real del codi. El pedaç de mico pot ser útil aquí, ja que pot modificar els mètodes dels serveis externs substituint-los per mètodes de marcador de posició que anomenem "estub". Aquests mètodes retornen el resultat esperat en els casos de prova, de manera que podem evitar iniciar sol·licituds als serveis de producció només pel bé de les proves.

L'exemple següent és una implementació senzilla del pedaç de Monkey al mòdul http natiu de NodeJ. El mòdul http és la interfície que implementa els mètodes de protocol http per a NodeJ. S'utilitza principalment per crear servidors http barebone i comunicar-se amb serveis externs mitjançant el protocol http.

A l'exemple següent tenim un cas de prova senzill on cridem a un servei extern per obtenir la llista d'identificacions d'usuari. En lloc de trucar al servei real, apliquem el mètode http get perquè només retorni el resultat esperat, que és una matriu d'identificacions d'usuari aleatòries. Això pot no semblar de gran importància ja que només estem obtenint dades, però si implementem un altre cas de prova que implica alterar dades d'algun tipus, podríem alterar accidentalment les dades de producció en executar proves.

D'aquesta manera podem implementar les nostres funcionalitats, i escriure proves per a cada funcionalitat tot garantint la seguretat dels nostres serveis de producció.

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

El codi anterior és senzill, importem el mòdul http, reassignem el mètode http.get amb un mètode nou que només retorna una matriu d'identificadors. Ara anomenem el nou mètode pegat dins del cas de prova i obtenim el nou resultat esperat.

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

Errors i limitacions comuns

No hauria de sorprendre que el pegat del mico tingui els seus propis defectes i limitacions. En el context dels mòduls del sistema de mòduls de nodes, l'aplicació de pedaços a un mòdul global com http es considera una operació amb efectes secundaris, perquè http és accessible des de qualsevol punt de la base de codi i qualsevol altra entitat pot dependre d'ell. Aquestes entitats esperen que el mòdul http funcioni amb el seu comportament habitual, canviant un dels mètodes http de manera efectiva trencarem totes les altres dependències http dins de la base de codi.

Com que estem operant dins d'un llenguatge tipificat dinàmicament, és possible que les coses no fallin immediatament i preferiria tenir un comportament impredictible que fa que la depuració sigui una tasca extremadament complexa. En altres casos d'ús, pot haver-hi dos pedaços diferents del mateix component en el mateix atribut, en aquest cas no podem predir realment quin pegat tindrà prioritat sobre l'altre, donant lloc a un codi encara més impredictible.

També és important esmentar que el pedaç de mico pot tenir lleugeres variacions en el comportament entre diferents llenguatges de programació. Tot depèn del disseny del llenguatge i de les opcions d'implementació. Per exemple, a Python, no totes les instàncies que utilitzen un mètode pegat es veuran afectades pel pedaç. Si una instància crida explícitament al mètode pegat, obtindrà la nova versió actualitzada, per contra, altres instàncies que només poden tenir atributs que apunten al mètode pegat i que no el cridin explícitament obtindran la versió original, això es deu a com python funciona vinculant a les classes.

Conclusió

En aquest article vam explorar les distincions d'alt nivell entre llenguatges de programació estàtics i dinàmics, vam veure com els llenguatges de programació dinàmics es poden beneficiar de nous paradigmes i patrons aprofitant la flexibilitat inherent que ofereixen aquests llenguatges. L'exemple que vam mostrar estava relacionat amb el pegat de Monkey, una tècnica que s'utilitza per ampliar el comportament del codi sense canviar-lo de la font. Vam veure un cas en què l'ús d'aquesta tècnica seria beneficiós juntament amb els seus inconvenients potencials. El desenvolupament de programari es tracta de compensacions, i emprar la solució adequada per al problema requereix consideracions elaborades per part del desenvolupador i una bona comprensió dels principis i fonaments de l'arquitectura.


Career Services background pattern

Serveis de carrera

Contact Section background image

Seguim en contacte

Code Labs Academy © 2024 Tots els drets reservats.