Cada lenguaje, cada tecnología, cada paradigma de programación persigue siempre la reutilización de código. En la comunidad de desarrollo se habla frecuentemente de DRY (Don’t Repeat Yourself) o WORE (Write Once Run Everywhere). Pero estos manidos mantras se quedan frecuentemente en una mera declaración de principios.
El código desarrollado para su reutilización no es capaz de reubicarse en otros contextos arquitectónicos de aquellos para los que fue inicialmente diseñado. Las capacidades de meta-programación de JavaScript le convierten en un lenguaje flexible y lo suficientemente plástico como para adaptarse dinámicamente a cualquier solución construida.
En esta charla exploramos como construir programas que se modifiquen a si mismos para resolver estos problemas y hablaremos de modelos de programación basados en componentes de software.
2. @javiervelezreye 2
Autor
Metaprogramación En JavaScript
Licenciado en informá1ca por la UPM desde el año 2001 y doctor
en informá1ca por la UNED desde el año 2009, Javier es
inves1gador y su línea de trabajo actual se centra en la innovación
y desarrollo de tecnologías de Componentes Web. Además realiza
ac1vidades de evangelización y divulgación en diversas
comunidades IT, es Polymer Polytechnic Speaker y co-organizador
del grupo Polymer Spain que conforma una comunidad de interés
de ámbito nacional en relación al framework Polymer y a las
tecnologías de Componentes Web.
I. ¿Quién Soy?
II. ¿A Qué Me Dedico?
javier.velez.reyes@gmail.com
@javiervelezreye
linkedin.com/in/javiervelezreyes
gplus.to/javiervelezreyes
jvelez77
javiervelezreyes
youtube.com/user/javiervelezreyes
Polymer Polytechnic Speaker
Co-organizador de Polymer Spain
Evangelización Web
Desarrollador JS Full stack
Arquitectura Web
Formación & Consultoría IT
e-learning
4. @javiervelezreye 4
Introducción
Metaprogramación En JavaScript
Qué Es La Metaprogramación
La metaprogramación es una disciplina de programación que
consiste en construir programas que generan, manipulan o
modifican otros programas, incluidos ellos mismos.
Definición
Programa Como Proceso de
Transformación De Datos
Metaprograma Como Proceso de
Transformación De Programas
El programa toma unos datos de
entrada y produce unos resultados de
salida de acuerdo a un proceso de
transformación
El metaprograma toma unos datos de
entrada que representan un programa
y valores de configuración para dirigir
el proceso de construcción de otro
programa
7. @javiervelezreye 7
Introducción
Metaprogramación En JavaScript
Por Qué La Metaprogramación
De esta manera se consigue reducir costes de desarrollo y mantenimiento por medio de la
economía de escala y la economía de alcance. La primera reduce costes por el aumento de la
producción de un solo producto. La segunda lo consigue por el abaratamiento de la adaptación
en productos similares.
Líneas de Producto. Economía de Escala & Economía de Alcance
Una línea de producción permite el
abaratamiento de costes por el desarrollo
en masa de un gran número de productos
idénBcos
La línea de variación consigue el
abaratamiento de costes por la
construcción de productos muy similares
Economía De Escala Economía De Alcance
Producto 1 Producto N
…
Proyecto A
Proyecto K
…
8. @javiervelezreye 8
Introducción
Metaprogramación En JavaScript
Por Qué La Metaprogramación
En términos generales, podemos resumir en cuatro los obje1vos principales de la
metaprogramación. Automa1zación, reu1lización, mantenibilidad y extensibilidad. Todos ellos
promueven, en mayor o menor medida, tanto la economía de alcance como la economía de
escala.
AutomaUzación, ReuUlización, Mantenibilidad & Extensibilidad
Automa;zación
Reu;lización
Extensibilidad
Mantenibilidad
El uso de componentes permite una
a d e c u a d a e s p e c i a l i z a c i ó n d e
responsabilidades que fomenta la
mantenibilidad por susBtución de
partes
La automaBzación persigue la idea de
construir soRware a través de procesos de
confección que minimizan los esfuerzos de
desarrollo
El carácter adaptaBvo o generaBvo de los
componentes les confiere una mayor
capacidad evoluBva lo que facilita la
extensibilidad global del sistema
La plasBcidad contractual de los
componentes se regenera o adapta
para permiBr que estos encajen
fácilmente en diversos contextos de
explotación arquitectónica
11. @javiervelezreye 11
Introducción
Metaprogramación En JavaScript
El Contexto De La Metaprogramación
En la metaprogramación está1ca las fases de elaboración y ejecución se desarrollan
secuencialmente mientras que en la elaboración dinámica el desarrollo de ambas fases es
paralelo. Esta segunda aproximación ofrece oportunidades de reelaboración que no son
posibles con la metaprogramación está1ca.
Metaprogramación EstáUca & Dinámica
Elaboración EstáUca
usa
Framework de Metaprogramación
Metacomponentes
Metaprogramas de
composición o generadores
Componentes de Proyecto
crea
Lenguaje de Programación
Lenguaje de Metaprogramación
Orquestador pasivo
Especificación
de producto
En la elaboración estáBca primero se
elaboran los componentes de cliente y
luego se lanzan a ejecución
La desventajas es que una
vez creados los compo-
nentes, éstos no pueden
readaptarse
Fase de Elaboración
Fase de Ejecución
12. @javiervelezreye 12
Introducción
Metaprogramación En JavaScript
El Contexto De La Metaprogramación
En la metaprogramación está1ca las fases de elaboración y ejecución se desarrollan
secuencialmente mientras que en la elaboración dinámica el desarrollo de ambas fases es
paralelo. Esta segunda aproximación ofrece oportunidades de reelaboración que no son
posibles con la metaprogramación está1ca.
Metaprogramación EstáUca & Dinámica
Elaboración Dinámica
Fase de Elaboración
Fase de Ejecución
En la elaboración dinámica la elaboración
de los componentes de cliente se realiza
en Bempo de ejecución
Esto permite que los
componentes se readap-
ten dinámicamente al
contextos cambiantes de
uso
usa
Framework de Metaprogramación
Metacomponentes
Componentes de Proyecto
crea
Lenguaje de Programación
Lenguaje de Metaprogramación
Especificación
de producto
Metaprogramas de
composición o generadores
Orquestador pasivo
13. @javiervelezreye 13
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
El proceso de construcción por
metaprogramación de un compo-
nente atraviesa un conjunto de
fases conceptuales con propósitos
diferenciales y caracterís1cos.
Ciclo De Elaboración Nivel de dominio
Nivel de proyecto
Descomposición
diseñada Selección
Adaptación
Composición
Encapsulación
Metacomponentes
Componente
C. Adaptado
Colaboración
C. Encapsulado
C. Contextualizado
Contextualización
Abstracción
Análisis
14. @javiervelezreye 14
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
La selección consiste en determinar las variantes específicas de cada 1po de meta-componente
que formarán parte del proceso construc1vo. La selección está1ca es aquella que se realiza
enteramente antes del 1empo de ejecución. Por el contrario, la selección dinámica implica que
el metaprograma inyecta lógica de negocio para ar1cular la selección en 1empo de ejecución
pudiendo hacer ésta dinámicamente cambiante.
Selección
Selección
Variantes
de producto
Selección Está;ca
La selección estáBca se resuelve antes del
Bempo de ejecución de manera que las
variantes seleccionadas quedan definiBva-
mente fijadas
Selección Dinámica
La ventaja de la selección dinámica es que
permite dar soporte a escenarios donde las
variantes uBlizadas pueden cambiar
durante el Bempo de ejecución
15. @javiervelezreye 15
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
La adaptación es el proceso mediante el cual un componente se adapta para resolver las
impedancias que presenta con el contexto arquitectónico de explotación. La adaptación
sintác1ca se centra en adaptar el contrato del componente mientras que la adaptación
semán1ca persigue modificar el comportamiento del artefacto a los requerimientos del
proyecto. Tanto estandarizar los componentes como trabajar con componentes proto1po a
nivel meta evita frecuentemente la adaptación.
Adaptación
Adaptación sintác;ca & semán;ca
Un ejemplo de adaptación sintácBca
consiste en cambiar los nombres de los
métodos de un componente. La decoración
por aspectos del comportamiento de un
método es un ejemplo de adaptación
semánBca
Estandarización & Proto;pos
La estandarización de contratos y comporta-
mientos evita la adaptación. Asimismo
cuando los metacomponentes son
componentes directamente operaBvos se
posibilita la ausencia ocasional de
adaptación
Proyecto A Proyecto B Proyecto C
Adaptación
16. @javiervelezreye 16
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
La composición es un ejercicio de transformación en el que un metacomponente al que
llamaremos núcleo se enriquece por la adquisición de cierta lógica parcial que reside en otro u
otros metacomponentes llamados extensiones. En el marco de esta relación binaria es posible
analizar el grado de dependencia que 1enen cada una de estas partes entre sí para caracterizar
el 1po de composición.
Composición
Composición Núcleo
Dependiente Autónomo
Dependiente
Autónomo
Extensión Composición
CooperaBva
Composición
Subordinada
Composición
Subordinante
Composición
Autónoma
Proyecto A
17. @javiervelezreye 17
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
La encapsulación es un proceso mediante el cual se establecen nuevas fronteras arquitectónicas
en el marco de una composición que 1enen por obje1vo aislar la misma del contexto de
explotación. Este aislamiento permite la protección de la información. Por extensión
consideraremos que todo mecanismo que garan1ce esta protección es una forma débil de
encapsulación.
Encapsulación
Encapsulación
Proyecto B Proyecto A
Encapsulación Fuerte
La encapsulación fuerte es el proceso
mediante el cual se levantan unas fronteras
arquitectónicas nuevas en el marco de una
relación composiBva
Encapsulación Débil
Por extensión, consideraremos que
cualquier mecanismo que proporcione
protección de información en el marco de la
composición es una forma de encapsulación
débil
18. @javiervelezreye 18
Introducción
Metaprogramación En JavaScript
El Proceso De La Metaprogramación
La contextualización es la fase úl1ma del proceso composi1vo mediante la cual se dota al
componente de una condiciones ambientales de contexto que condicionarán su
comportamiento durante el 1empo de ejecución. Cuando estas condiciones ambientales se fijan
antes del 1empo de ejecución hablamos de contextualización está1ca. La variante dinámica
permite ar1cular recontextualizaciones en 1empo de ejecución.
Contextualización
Contextualización
Contextualización Está;ca
La contextualización estáBca resuelve las
condiciones ambientales de contexto antes
de que el componente entre en el Bempo de
ejecución.
Contextualización Dinámica
La contextualización dinámica permite
restablecer recurrentemente el contexto de
ejecución lo que ofrece mayores oportu-
nidades de adaptación
Estado
Lógica A
B
C
24. @javiervelezreye 24
JavaScript Como Lenguaje De Metaprogramación
Metaprogramación En JavaScript
Introducción
Como discu1remos, JavaScript es un buen candidato como
lenguaje para cubrir las necesidades propias de los procesos de
metaprogramación. En efecto, su carácter dinámico, su sistema
de 1pificación débil y su sistema de clases basado en proto1pos
confieren a los artefactos construidos un alto grado de plas1cidad
adapta1va que facilita los procesos de metaprogramación.
Capacidades Del Lenguaje
Sin embargo, aquí restringiremos nuestros estudio a
las capacidades del lenguaje en el marco de un
modelo de componentes basado en objetos. En este
sen1do comprobaremos que las álgebras de objetos
resultan muy flexibles y potentes para ar1cular
procesos de metaprogramación. Quedan fuera las
capacidades del lenguaje vinculadas a la
programación funcional que fomentan un es1lo más
genera1vo que composi1vo.
Modelo De Objetos
Tipificación Débil
ProtoUpos
Dinamicidad
JavaScript
Modelo de Objetos
Modelo de Funciones
Delegación
Herencia
Polimorfismo
Ligadura Dinámica
Orden Superior
Clausuras
Evaluación Parcial
26. @javiervelezreye 26
JavaScript Como Lenguaje De Metaprogramación
Metaprogramación En JavaScript
Capacidades De JavaScript En El Modelo De Objetos
Por medio de la delegación se ar1culan esquemas de
colaboración entre objetos de manera que los algoritmos se
distribuyen entre el ecosistema de objetos par1cipantes. Esto
permite ar1cular una adecuada división de responsabilidades en
las fases preliminares de diseño.
La Delegación En La Composición
Dynamic Binding
var a = {
d: b,
m: function (){
d.n();
}
};
var b = {
n: function (){
...
}
}
b.n = function () {
...
};
El enlace dinámico permite resolver la
localización del método llamado justo antes de
la invocación lo que se alinea con el modelo de
objetos abiertos
var b = {
};
Enlace dinámico
b
{
n: function (){
...;
}
}
El esquema de delegación impone una
parte estáBca, que determina el
nombre del método a invocar y otra
dinámica que informa del objeto
donde reside dicho método
a
{
d: B,
m: function (){
d.n();
}
}
Delegación
28. @javiervelezreye 28
JavaScript Como Lenguaje De Metaprogramación
Metaprogramación En JavaScript
Capacidades De JavaScript En El Modelo De Objetos
En JavaScript el polimorfismo sólo puede interpretarse en
términos de la conformidad a un contrato establecido por
convenio. A diferencia de otros lenguajes, esta conformidad
puede ser total o parcial.
El Polimorfismo En La Composición
Duck Typing
Para garanBzar que o es compaBble con el
protocolo de c, se debe imponer la existencia
de un método m en o. La metaprogramación
puede transformar o en este senBdo
Duck Typing
Los objetos a y b resultan conformes a
un contrato definido por la existencia
del método m. Esto es una forma de
polimorfismo débil que no está
arBculada a través de herencia ni de
definición de interfaces
Conformidad Contractual
b
{
m: function () {},
y: function () {}
}
var o = {
};
c.m(o);
var c = {
m: function (o){
o.m();
}
};
var o = {
m: function () {}
};
c.m(o);
o.m = function () {
...
};
a
{
m: function () {},
x: function () {}
}
30. @javiervelezreye 30
JavaScript Como Lenguaje De Metaprogramación
Metaprogramación En JavaScript
Capacidades De JavaScript En El Modelo De Objetos
Los métodos del modelo de objetos se diferencian de las
funciones en el uso de un parámetro implícito – this – que
referencia al contexto donde dicho método debe ejecutarse.
Dicho contexto puede ser alterado por diversos mecanismos
explícitos o implícitos del lenguaje.
La Contextualización En La Composición
Puntero Implícito this
Las capacidades de recontextualización que
ofrece JavaScript permiten arBcular diversos
esquemas de conexión composiBva que
condicionan la variante funcional que se va a
invocar en cada caso
Recontextualización
bind permite especificar que el
método m debe ejecutarse en el
contexto del objeto b. Esto Bene
repercusiones acerca de a quien
afecta el código dentro de m y
permite arBcular composiciones
interesantes
Contextualización
os[2].m = function () {
...
};
var os = [a, b, c];
app.all (os);
var app = {
all: function (os){
for (o in os)
o.m();
}
};
var os = [a, b, c];
app.all (os);
a
{
m: function () {
return this.n();
}.bind (b)
}
b
{
n: function () {}
}
31. @javiervelezreye 31
JavaScript Como Lenguaje De Metaprogramación
Metaprogramación En JavaScript
Capacidades De JavaScript En El Modelo De Objetos
La mayoría de los metaprogramas hacen uno de las capacidades reflexivas del lenguaje. Una
arquitectura reflexiva es aquella que ofrece al programador ciertos metadatos para permi1rle
descubrir las caracterís1cas de los elementos del entorno de ejecución. A con1nuación
resumimos las capacidades esenciales de reflexión en JavaScript.
La Reflexión En La Composición
A
B
{
p1: v1,
...
pn: vn,
m1: function (){},
...
mn: function (){}
}
Object.keys (o),
Object.getOwnPropertyNames (o),
Object.freeze (o)
Object.seal (o)
Object.preventExtensions (o)
Object.isFrozen (o)
Object.isSeal (o)
Object.isExtensible (o)
Reflexividad a Nivel de Objeto
Object.defineProperty (o, p, {})
Object.defineProperties (o, {})
Object.getOwnPropertyDescriptor (o, p)
o.hasOwnProperty (p)
o.propertyIsEnumerable (p)
Reflexividad a Nivel de Propiedad
Object.getPrototypeOf (o)
o1.isPrototypeOf (o2)
o.prototype
o.constructor
o.__proto__
Reflexividad a Nivel de Proto;po
33. @javiervelezreye 33
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Introducción
La metaprogramación composi1va se caracteriza por el hecho de
que man1ene la estructura esencial de los componentes. Todo
ejercicio metaprogramá1co en este sen1do se reduce a un
conjunto de ac1vidades de intervención puntual que pretenden
adaptar, conectar o contextualizar código de los componentes.
La Metaprogramación ComposiUva Como Intervención
El conjunto potencial de intervenciones sobre un
modelo de componentes queda impuesto por las
capacidades metaprogramá1cas del lenguaje y el
modelo seleccionado. A lo largo de este capítulo nos
centraremos en objetos aunque es fácil hacer una
adaptación de los metaprogramas para que operen
sobre las álgebras funcionales. Diremos que cada
punto de intervención dentro de un componente es
un code holder mientras que el conjunto de todos
ellos conforman el contrato de composición sobre
modelo el componente.
Code Holders & Contratos De Composición
Transformación
restringida Intervención
puntual
Contrato de composición
Code holder o punto
de intervención
Componente
35. @javiervelezreye 35
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Introducción
A diferencia de lo que ocurre en otras aproximaciones de
composición, en JavaScript se impone que el modelo de
componente y metacomponente coincidan. Es decir, un
metacomponente es en realidad un componente
convencional que funciona como ejemplar protocpico para
confeccionar componentes opera1vos al nivel de
programación.
Metacomponentes Como Componentes ProtoUpo
En el campo de la metaprogramación composi1va, los
meta-programas se caracterizan por recibir como entrada
cada una de las partes par1cipantes en el proceso
composi1vo – núcleo y extensiones. El código del
metaprograma opera sobre estos elementos de manera
exógena. Es decir, éste no se incluye dentro de los
mismos. En aproximaciones genera1vas el código del
metaprograma coincide con el del componente
resultante. Este 1po de meta-programación se conoce
como endógena.
Metaprogramación Exógena
Metaprograma
composiUvo IN
OUT
Nivel de
Metaprogramación
Metacomponente Componente
=
Nivel de
Programación
36. @javiervelezreye 36
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Adición
Las técnicas adi1vas persiguen ampliar la lógica de un
componente con nueva funcionalidad. De esta manera el
componente se adapta a nuevos contextos arquitectónicos
donde la lógica adicionada es requerida.
Definición
Los puntos de intervención en este caso
reflejan el carácter abierto de los objetos en
JavaScript con lo que es posible alterar el
conjunto de caracterís1cas de los mismos.
Code Holders
Componente
original
Adición
metaprogramáBca
Objeto
{
p1: v1,
...
pn: vn,
m1: function (){},
...
mn: function (){}
}
Holders de
Propiedad
Holders de
Método miembro
37. @javiervelezreye 37
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Adición
Los operadores de composición fundamentales que hacen
uso de técnicas adi1vas permiten añadir, borrar, actualizar y
renombrar las caracterís1cas – métodos y propiedades – de
un objeto.
Operadores De Composición Por Adición
Los operadores fundamentales se centran en
operar sobre el conjunto de caracterísBcas
de un componente que funciona como
núcleo.
Operadores fundamentales de Adición
mp.add (o, 'x', 1);
mp.remove (o, 'x')
mp.update (o, 'x', 1);
mp.add (o, 'x', 1);
mp.rename (o, 'x', 'y');
mp.update (o, 'x', 5);
{}
{ x: 1 }
{}
{}
{ x: 1 }
{ y: 1 }
{ y: 5 }
Operador o
mp.add = function add (core, key, value) {
core[key] = value;
};
mp.remove = function remove (core, key) {
delete core[key];
};
mp.update = function update (core, key, value) {
if (core[key])
mp.add (core, key, value);
};
mp.rename = function rename (core, oldKey, newKey) {
mp.add (core, newKey, core[oldKey]);
mp.remove (core, oldKey);
};
38. @javiervelezreye 38
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Adición
Como extensión a los operadores de adición se pueden
incluir primi1vas que permiten copiar y mover propiedades
de un objeto a otro o extender un objeto core con las
capacidades de otro como extensión.
Operadores De Composición Por Adición
Los operadores fundamentales se comple-
mentan con otro conjunto de operadores que
implican un núcleo y una extensión
Otros operadores de Adición mp.copy = function copy (core, ext, key) {
mp.add (core, key, ext[key]);
};
mp.move = function move (core, ext, key) {
mp.copy (core, ext, key);
mp.remove (core, key);
};
mp.extend = function extend (core, ext) {
var keys = Object.getOwnPropertyNames (ext);
keys.forEach (function (key) {
mp.copy (core, ext, key);
});
};
mp.copy (o2, o1, 'y');
mp.remove (o2, 'y');
mp.move (o2, o1, 'x');
mp.extend (o1, o2);
{ y: 5 } {}
{ y: 5 } { y: 5 }
{ y: 5 } {}
{} { y: 5 }
{x: 0} {y: 0}
{x: 0, y: 0} {y: 0}
Operador o1 o2
39. @javiervelezreye 39
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Extensión
Las técnicas extensivas 1enen por objeto reformular las
capacidades funcionales del componente por medio de la
especialización semán1ca. Este 1po de técnicas se encuentra
estrechamente vinculado a la herencia por proto1pos como
mecanismo del lenguaje.
Definición
Sobre una jerarquía de herencia es posible
alterar la posición rela1va de los elementos
o reescribir la semán1ca de los métodos
miembros del hijo. Además se puede operar
sobre los enlaces de la cadena de proto-
1pado.
Code Holders
Componente
original
Extensión
metaprogramáBca
Objeto
{
p1: v1,
...
pn: vn,
m1: function (){},
...
mn: function (){}
}
Holders de
Movimiento
Holders de
sobrescritura
ProtoUpo
[[Prototype]]
Holder de Enlace
a ProtoBpo
40. @javiervelezreye 40
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Extensión
Los operadores fundamentales que hacen uso de técnicas
extensivas están relacionados con la alteración de la cadena
de proto1pado. Es posible añadir, borrar o inver1r el sen1do
de esta relación.
Operadores De Composición Por Extensión
Mediante estrategias reconstrucBvas es
posible cambiar dinámicamente el valor de
la relación de protoBpado entre objetos
Operadores fundamentales de Extensión mp.setPrototype = function (core, proto) {
var ch = Object.create (proto);
mp.extend (ch, core);
return ch;
};
mp.removePrototype = function (core) {
return mp.setPrototype (core, null);
};
mp.reversePrototype = function (core) {
var proto = Object.getPrototypeOf (core);
return mp.setPrototype (proto, core);
};
mp.setPrototype(o, p);
mp.removePrototype (o);
mp.reversePrototype (o);
o -> p
o
o <- p
Operador o p
41. @javiervelezreye 41
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Extensión
Los operadores de copia y movimiento por metaprogra-
mación permiten mover y copiar ascendente y descenden-
temente caracterís1cas presentes entre un proto1po y cada
uno de sus objetos hijos.
Operadores De Composición Por Extensión
Las operaciones de copia y movimiento de
caracterísBcas por la cadena de protoBpado
permiten alterar la semánBca definida por la
relación jerárquica entre objetos
Operadores fundamentales de Extensión
Operador o p
mp.copyUp = function (core, key) {
var proto = Object.getPrototypeOf (core);
if (proto)
mp.add (proto, key, core[key]);
};
mp.copyDown = function (core, key, child) {
mp.add (child, key, core[key]);
};
mp.moveUp = function (core, key) {
mp.copyUp (core, key);
mp.remove (core, key);
};
mp.moveDown = function (core, key, child) {
mp.add (child, key, core[key]);
mp.remove (core, key);
};
mp.copyUp (o, 'x');
mp.copyDown (p, 'x', o);
mp.moveUp (o, 'x');
mp.moveDown (p, 'x', o);
o{x} -> p, o{x} -> p{x}
o -> p{x}, o{x} -> p{x}
o{x} -> p, o -> p{x}
o -> p{x}, o{x} -> p
42. @javiervelezreye 42
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Intercesión
La intercesión permite inyectar lógica de decoración de
manera entrelazada con el código original del componente.
El propósito de este 1po de técnicas es ampliar la semán1ca
del mismo sin extender su contrato.
Definición
En cuanto a propiedades se puede inyectar
código para que se ejecute antes y después
de acceder a las mismas tanto para su
lectura como para su escritura. En los
métodos, existe similarmente decoración
anterior y posterior a la invocación, pero
además también es posible inyectar código
que se ejecutará cuando el método
explícitamente lo demande.
Code Holders
Componente
original
Intercesión
metaprogramáBca
Objeto
{
p1: v1 ,
pn: vn ,
m: function (){
<<code>>
}
}
Holder before
de acceso
Holder before
de invocación
Holder aRer
de invocación
Holder around
de invocación
Holder aRer
de acceso
43. @javiervelezreye 43
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Intercesión
Los operadores de intercesión fundamentales permiten
decorar la invocación de métodos inyectando lógica
funcional que se ejecuta antes o después de la ac1vación del
método.
Operadores De Composición Por Intercesión
Los operadores fundamentales de intercesión
permiten inyectar lógica funcional que se
ejecutará antes o después del método
Operadores fundamentales de Intercesión
Operador o
mp.before = function before (core, key, ext) {
var fn = core[key];
core[key] = function () {
ext.apply (this, arguments);
return fn.apply (this, arguments);
};
};
mp.after = function after (core, key, ext) {
var fn = core[key];
core[key] = function () {
var r = fn.apply (this, arguments);
ext.apply (this, arguments);
return r;
};
};
mp.before (o, 'f', g);
mp.after (o, 'f', g);
{f: function() {...} }
f, g
g, f
44. @javiervelezreye 44
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Intercesión
Adicionalmente, y como una especialización de los
operadores anteriores, se puede decorar un método en
before para condicionar el su ejecución en función del
resultado emi1do por una extensión de guarda.
Operadores De Composición Por Intercesión
Los operadores de guarda uBlizan técnicas de
intercesión para condicionar la ejecución de un
método en función del resultado emiBdo por
cierto predicado lógico
Operadores fundamentales de Intercesión
Operador o
mp.provided = function provided (core, key, ext) {
var fn = core[key];
core[key] = function () {
if (ext.apply (this, arguments))
return fn.apply (this, arguments);
};
};
mp.except = function except (core, key, ext) {
var fn = core[key];
core[key] = function () {
if (!ext.apply (this, arguments))
return fn.apply (this, arguments);
};
};
mp.provided (o, 'f', p);
mp.except (o, 'f', p);
{f: function() {...} }
f sii p es true
f sii p es false
46. @javiervelezreye 46
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Intercesión
Los operadores de acceso a propiedades para lectura y
escritura en before permiten inyectar código que se
ejecutará antes del acceso a las propiedades.
Operadores De Composición Por Intercesión
Se decoran los métodos de acceso y
modificación a propiedades get y set y se
manBene una variable oculta para soportar el
estado de la propiedad.
Operador explícito de Intercesión
Operador o
La extensión, g, se antepone
como primer parámetro a los
argumentos de la función
decorada f
mp.beforeGetAtt = function (core, key, ext) {
Object.defineProperty (core, '_' + key, {
value: core[key],
enumerable: false, ... });
Object.defineProperty (core, key, {
get: function () {
ext.call (this, arguments);
return core['_' + key];
}
});
};
mp.beforeSetAtt = function (core, key, ext) {
Object.defineProperty (...);
Object.defineProperty (core, key, {
set: function (v) {
ext.call (this, v);
core['_' + key] = v;
}
});
};
mp.beforeGetAtt (o, 'x', f)
mp.beforeSetAtt (o, 'x', f)
{x: 0}
o.x -> f, 0
o.x = 3 -> f, {x: 3}
48. @javiervelezreye 48
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Delegación
Las técnicas basadas en delegación ar1culan estrategias
composi1vas que permiten dispersar la lógica de un servicio
entre varios componentes con iden1dad propia dentro de la
arquitectura pero estableciendo fachadas de desacopla-
miento.
Definición
Los puntos de intervención en esta familia
de técnicas coinciden con los de la
intercesión. La diferencia estriba en este
caso en que los metaprogramas inyectan
código dirigido a establecer esquemas
delega1vos a otra lógica.
Code Holders
Objeto
{
p1: v1 ,
pn: vn ,
m: function (){
<<code>>
}
}
Holder before
de acceso
Holder before
de invocación
Holder aRer
de invocación
Holder around
de invocación
Holder aRer
de acceso
Componente
original
Delegación
metaprogramáBca
49. @javiervelezreye 49
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Delegación
Este 1po de operadores ar1culan procesos de composición
por delegación interna y externa mediante la construcción
de métodos proxy añadidos al core.
Operadores De Composición Por Delegación
La delegación interna permite generar un
método de proxy que delega en otro
método del mismo objeto. La delegación
externa se produce entre objetos
diferentes
Operador fundamentales de Delegación
Operador o1 [o2]
mp.innerDelegate = function (core, oKey, nKey, ctx) {
var context = ctx || core;
core[nKey] = function () {
return core[oKey].apply (context, arguments);
};
};
mp.outerDelegate = function (core, ext, key, ctx) {
var context = ctx || ext;
core[key] = function () {
var r = ext[key].apply (context, arguments);
return r === ext ? this : r;
};
};
mp.innerDelegate(o1,'f','g')
mp.outerDelegate(o1,o2,'f')
{f}
{g:function(){o1.f()}
f}
{} {f}
{f:function(){o2.f()}}
{f}
50. @javiervelezreye 50
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Delegación
A par1r de los operadores anteriores se definen los
operadores de forwarding y proxy en forwarding que
contextualizan la invocación en el delegado.
Operadores De Composición Por Delegación
El forwarding es una modalidad de
delegación débil contextualizada en el
delegado. Es lo que se conocer por
delegación convencionan en los lenguajes
de OOP
Operador fundamentales de Delegación
Operador o1 [o2]
mp.forward = function (core, ext, key) {
mp.outerDelegate (core, ext, key, ext);
};
mp.forwardProxy = function (core, ext) {
if (typeof(ext) === 'string') ext = core[ext];
if (ext) {
var keys = Object.keys (ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function')
mp.forward (core, ext, key);
});
}
};
mp.forward (o1,o2,'f')
o1.f()
{x:1}{x:3,
f(){return this.x}}
3
51. @javiervelezreye 51
Técnicas De Metaprogramacion Composi1va
Metaprogramación En JavaScript
Técnicas De Metaprogramación Por Delegación
Similarmente, los operadores fundamentales pueden
u1lizarse para ar1cular técnicas de delegación contextual-
lizadas en el core.
Operadores De Composición Por Delegación
La delegación es la forma más fuerte y
dinámica de vinculación composiBva. En
ella el contexto de evaluación permanece
en el objeto core
Operador fundamentales de Delegación
Operador o1 [o2]
mp.delegate = function (core, ext, key) {
mp.outerDelegate (core, ext, key, core);
};
mp.delegateProxy = function (core, ext) {
if (typeof(ext) === 'string') ext = core[ext];
if (ext) {
var keys = Object.keys (ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function')
mp.delegate (core, ext, key);
});
}
};
mp.delegate (o1,o2,'f')
o1.f()
{x:1}{x:3,
f(){return this.x}}
1
53. @javiervelezreye 53
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Introducción
Existen diversos modelos arquitectónicos de metaprogramación composi1va que basan su
implementación en cada uno de las cuatro técnicas descritas en el capítulo anterior. En este
capítulo describimos dichos modelos.
Modelos Arquitectónicos Según Técnicas ComposiUvas
Mixins Based
Programming
Traits Based
Programming
Adición
Aspect Oriented
Programming
Intercesión
Role
Programming
ComposiBon
Filters
Delegación
Adap1ve
Programming
Subject Orieted
Programming
Extensión
Layered
Objects
55. @javiervelezreye 55
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
Los mixins generan problemas potenciales de colisión de
estado ya que varios mixins pueden u1lizar los mismos
atributos de soporte. Encapsular el estado permite evitarlo.
Mixins. Colisión de Estado
var john = {
first: 'John',
last : 'Doe'
};
mp.mixin (john,isAutor)
Mixin A
Mixin B
Core
Adición Adición
{s}
var isAuthor = {
data: [],
addBook: function (name) {
this.data.push(name);
return this;
},
getBooks: function () {
return this.data;
}
};
var isParent = {
data: [],
addChild: function (name) {
this.data.push(name);
return this;
},
getChildren: function () {
return this.data;
}
};
mp.mixin = function (core, ext) {
var context = Object.create (null);
var keys = Object.getOwnPropertyNames(ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function') {
mp.copy (core, ext, key);
mp.bind (core, key, context);
}
else mp.copy (context, ext, key);
});
};
mp.mixin (john,isParent)
56. @javiervelezreye 56
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
En los mixins con estado protegido existe la necesidad de
referir al core desde el cuerpo de sus métodos. Ello requiere
meter una referencia al core – self – en el contexto.
Mixins. Self & This
Mixin A
Mixin B
Core
Adición Adición
{s}
var john = {
first: 'John',
last : 'Doe'
};
var isAuthor = {
data: [],
addBook: function (name) {
this.data.push(name);
return this;
},
getBooks: function () {
return this.data;
}
description: function () {
return 'Books:' + this.getBooks ();
}
};
mp.mixin (john,isAutor)
this.self.getBooks ();
mp.mixin = function (core, ext) {
var context = { self : core };
var keys = Object.getOwnPropertyNames(ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function') {
mp.copy (core, ext, key);
mp.bind (core, key, context);
}
else mp.copy (context, ext, key);
});
};
57. @javiervelezreye 57
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
En los mixins con estado protegido existe la necesidad de
referir al core desde el cuerpo de sus métodos. Ello requiere
meter una referencia al core – self – en el contexto.
Mixins. Colisión de Métodos
Mixin A
Mixin B
Core
Adición Adición
{s}
mp.mixin (john,isAutor)
mp.mixin (john,isParent)
var john = {
first: 'John',
last : 'Doe'
};
var isAuthor = {
init: function () {
this.books = [];
},
addBook: function (name) {
this.books.push(name);
return this;
},
getBooks: function () {
return this.books;
}
};
var isParent = {
init: function () {
this.children = [];
},
addChild: function (name) {
this.children.push(name);
return this;
},
getChildren: function () {
return this.children;
}
};
mp.mixin = function (core, ext, policy) {
var context = { self : core };
var decorate = policy || mp.after;
var keys = Object.getOwnPropertyNames(ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function') {
if (core[key]) {
decorate (core, ext, key);
mp.bind (core, key, context);
} else {
mp.copy (core, ext, key);
mp.bind (core, key, context);
}
} else mp.copy (context, ext, key);
}); };
58. @javiervelezreye 58
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
Para resolver los problemas de colisión de nombres entre
métodos de diferentes mixins se puede aplicar una variedad
de polí1cas de ges1ón de colisiones. Estás polí1cas aplican
en esencia técnicas de entrelazado funcional entre los
métodos en conflicto. A con1nuación mostramos aquellas
más protocpicas.
Mixins. PolíUcas de Resolución de Conflictos
mp.override = function (core, key, ext) {
core[key] = function () {
return ext.apply (this, arguments);
};
},
mp.discard = function (core, key, ext) {
var fn = core[key];
core[key] = function () {
return fn.apply (this, arguments);
};
},
mp.before = function before (core, key, ext) {
var fn = core[key];
core[key] = function () {
ext.apply (this, arguments);
return fn.apply (this, arguments);
}; };
mp.after = function after (core, key, ext) {
var fn = core[key];
core[key] = function () {
var r = fn.apply (this, arguments);
ext.apply (this, arguments);
return r;
};
};
mp.around = function around (core, key, ext) {
var fn = core[key];
core[key] = function () {
var args = [].slice.call (arguments);
args = [ext.bind (this)].concat(args);
return fn.apply (this, args);
};
};
Mixin A
Mixin B
Core
Adición PolíBca
Adición
59. @javiervelezreye 59
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
Dado que los métodos sinónimos se resuelven por
intercesión es necesario discriminar qué parámetros
corresponden a cada método en conflicto tras la intercesión.
Para ello pasamos a un esquema nominal.
Mixins. Colisión de Parámetros
Mixin A
Mixin B
Core
Adición Adición
{defaults}
{defaults}
var john = {
first: 'John',
last : 'Doe'
};
var isAuthor = {
init: function (bks) {
this.books = bks || [];
},
addBook: function (name) {
this.books.push(name);
return this;
},
getBooks: function () {
return this. Books;
}
};
mp.mixin (john,isAutor) mp.mixin (john,isParent)
var isParent = {
init: function (chs) {
this.children = chs || [];
},
addChild: function (name) {
this.children.push(name);
return this;
},
getChildren: function () {
return this.children;
}
};
init: function (defaults) {
defaults = defaults || {};
this.children = defaults.children || [];
},
init: function (defaults) {
defaults = defaults || {};
this.books = defaults.books || [];
},
60. @javiervelezreye 60
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Adición
Cuando se crean proto1pos por mixtura el estado privado se
comparte entre todos los hijos. Para solucionar este
problema debemos modificar el metaprograma para crear
un estado en cada hijo.
Mixins. Adición sobre ProtoUpos
Mixin A
O1
{s} {s}
Prototype
O2
var john = {
first: 'John',
last : 'Doe'
};
var isAuthor = {
init: function () {
this.books = [];
},
addBook: function (name) {
this.books.push(name);
return this;
},
getBooks: function () {
return this. Books;
}
};
var isParent = {
init: function () {
this.children = [];
},
addChild: function (name) {
this.children.push(name);
return this;
},
getChildren: function () {
return this.children;
}
};
mp.mixin = function (core, ext, policy) {
var context = mp.key ();
core[context] = { self: core };
var decorate = policy || mp.after;
var keys = Object.getOwnPropertyNames(ext);
keys.forEach (function (key) {
if (typeof (ext[key]) === 'function') {
if (core[key])
decorate (core, key, ext[key]);
else
mp.outerDelegate (core, ext, key,
core[context]);
}
else mp.copy (core[context], ext, key);
});
};
var Person = Object.create (null)
mp.mixin (Person, isAutor)
mp.mixin (Person, isParent)
var john = Object.create (Person)
Person
61. @javiervelezreye 61
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Intercesión
Los aspectos son abstracciones funcionales encapsuladas
que se aplican transversalmente a los métodos miembros de
un core. La polí1ca de intercesión empleada forma parte de
la encapsulación.
Aspectos. Intercesión EstáUca
Aspect A
Mixin B
Intercesión
Intercesión
Core var checkable = {
add: {
when: 'before',
advice: function (id, v) {
if (typeof (v) !==
'number')
throw 'Invalid Type';
}
}
};
var loggable = {
add: {
when: 'before',
advice: function (id, value) {
console.log ('Adding', id);
}
},
find: {
when: 'after',
advice: function (id) {
console.log ('Find', id);
}
}
};
var manager = {
data : {},
find : function (id) {
return this.data[id];
},
add : function (id, value) {
this.data[id] = value;
}
};
mp.aspect (manager, checkable);
mp.aspect (manager, loggable);
mp.sAspect = function (core, ext){
var ks = Object.getOwnPropertyNames(ext);
ks.forEach (function (key) {
var m = ext[key];
mp[m.when](core, key, m.advice);
});
};
62. @javiervelezreye 62
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Intercesión
Los aspectos son abstracciones funcionales encapsuladas
que se aplican transversalmente a los métodos miembros de
un core. La polí1ca de intercesión empleada forma parte de
la encapsulación.
Aspectos. Intercesión Dinámica
mp.dAspect = function (core, ext){
var ks= Object.getOwnPropertyNames (ext);
ks.forEach (function (key) {
if (!core[key].isAdvisable)
mp.advisable (core, ext, key);
var m = ext[key];
m[ext[key].when](ext[key].advice);
});
};
mp.advisable = function (core, ext, key) {
var core[key] = function () {
var args = [].slice.call (arguments);
method.befores.forEach (function (fn) {
fn.apply (this, args);
});
method.body.apply (this, args);
method.afters.forEach (function (fn) {
fn.apply (this, args);
});
}
core[key].isAdvisable = true;
core[key].befores = [];
core[key].body = core[key];
core[key].afters = [];
core[key].before = function (fn) {
this.befores.unshift (fn); };
method.after = function (fn) {
this.afters.push (fn); };
core[key] = method;
};
Aspect A
Mixin B
Intercesión
Intercesión
Core
63. @javiervelezreye 63
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Delegación
El modelo arquitectónico de filtros composi1vos persigue
enriquecer cada objeto por intercesión para ar1cular
delegaciones que ges1onen en tratamiento de mensajes.
Filtros de Composición
{pn}
f
g
…
{s}
El tratamiento que hacen los
filtros de los mensajes consiste en
arBcular una delegación interna o
externa a otros componentes
presenten en el contexto
arquitectónico
Delegación Interna y Externa
Cada método se decora con filtros
secuenciales de intercesión en before y
aRer que persiguen alterar la semánBca
del mismo gesBonando el tratamiento
de los mensajes
Filtros de Entrada y Salida
Los filtros determinan las acciones
de gesBón de mensajes a realizar en
función de cierto estado y
predicados de control que se añaden
al componente
E s t a d o y
Predicados
64. @javiervelezreye 64
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Delegación
Los aspectos son abstracciones funcionales encapsuladas
que se aplican transversalmente a los métodos miembros de
un core. La polí1ca de intercesión empleada forma parte de
la encapsulación.
Filtros de Composición
DB 0
DB 1 DB 2
replicar
delegar
replicar
delegar
var fRound = function (j, dbs){
return {
state: {
active: false,
dbs: dbs,
next: (j+1) % dbs.length
},
advices: {
get: {
when: 'provided',
advice: function (id) {
if (this.active) {
this.active = false;
this.dbs[this.next].active = true;
return true;
} else {
this.dbs[this.next].get(id);
return false;
} ...
};
var fCopy = function (j){
return {
state: { },
advices: {
set: {
when: 'before',
advice: function (id, obj) {
if (j < this.dbs.length - 1)
this.dbs[j+1].set(id, obj);
}
}
}
};
};
65. @javiervelezreye 65
Modelos Arquitectónicos De Composición
Metaprogramación En JavaScript
Modelos Arquitectónicos Basados En Delegación
Los aspectos son abstracciones funcionales encapsuladas
que se aplican transversalmente a los métodos miembros de
un core. La polí1ca de intercesión empleada forma parte de
la encapsulación.
Filtros de Composición. Colaboración entre Filtros
DB 0
DB 1 DB 2
replicar
delegar
replicar
delegar
var db = {
get: function (id){...},
set: function (id, obj){...}
};
var dbs = [Object.create (db),
Object.create (db),
Object.create (db)];
dbs.forEach (function (db, i, dbs) {
mp.filter (db, fRound (i, dbs));
mp.filter (db, fCopy (i));
});
[1][1][1]
[1,2][1,2][1,2]
[1,2,3][1,2,3][1,2,3]
Getting 1 from DB0...
Delegating Get to DB1...
Getting 2 from DB1
Se inicializa el array de
bases de datos y se
establecen los filtros
Construcción
Siempre se ataca la base de datos DB0. Cada
operación set propaga una replica a la
siguiente base de datos. La gesBón de queries
se resuelve en round robin
Funcionamiento
dbs[0].active = true;
dbs[0].set (1, 1);
dbs[0].set (2, 2);
dbs[0].set (3, 3);
dbs[0].get (1);
dbs[0].get (2);
mp.filter = function (core, ext) {
mp.extend (core, ext.state);
mp.sAspect (core, ext.advices);
};