Categorías
Javascript

(kaop) Programación orientada a aspectos con ES5

Buenas a todos.

Vengo para presentar esta librería: https://www.npmjs.com/package/kaop

Vamos directos al grano. La programación orientada a aspectos es un paradigma de desarrollo que enfatiza la encapsulación de funcionalidades y, por tanto, su consecuente abtracción. Una vez tenemos diseñadas nuestras funcionalidades podemos «anotar» o «decorar» (se puede llamar de muchas maneras) nuestra clase con estas funcionalidades que van a modificar su comportamiento. Este formato tiene sus ventajas y desventajas que no procede enumerar ahora. Sólo comentar brevemente las impresiones de trabajar con este este paradigma:

Desarrollos atómicos: respetar este patrón nos permite abordar desarrollos de manera muy concreta y abstraer las metas del resto de la lógica de la aplicación. De esta manera tocaremos ficheros con pocas lineas muy fáciles de leer. El desarrollador tendrá claro que tiene que desarrollar y sabrá que lo terminará antes de finalizar su jornada laboral, y en todo momento sabrá cual es su meta.

Escalabilidad: nuestro código será fácil de extender, facil de meter mano. Siempre y cuando tengamos claro lo que queremos hacer. Si sómos puristas con este patrón veremos que en nuestro código no deberían aparecer «boilerplates», u ollas a presión, que son lugares que todos conocemos, en aplicaciones grandes, que ni en vuestras peores pesadillas os molaría meter mano (mucho código, con funcionalidades entrelazadas, que ha tocado mucha gente y nadie sabe que es lo que hace, pero funciona… hasta que lo tocas).

Legibilidad: lo verdaderamente atractivo de este patrón es que la mayor parte del código de nuestra aplicación es cómo un libro de dora la exploradora x).

    var Person = Class({
      constructor: function(name, dborn) {
        this.name = name;
        this.dborn = dborn;
      },
      run: function() {
        return "Im running!";
      },
      getAge: function() {
        var currentYear = new Date().getFullYear();
        var yearBorn = this.dborn.getFullYear();
        return currentYear - yearBorn;
      }
    });
    var Programmer = Class.inherits(Person, {
      constructor: ["$override", function(parent, name, dborn, favouriteLanguage) {
        parent(name, dborn);
        this.favLang = favouriteLanguage;
      }],
      run: ["$override", function(parent) {
        return parent() + " but... not as faster, coz im fat :/";
      }],
      code: function() {
        return "Im codding in " + this.favLang;
      }
    });

kaop provee de funcionalidades para simular la creación de clases y herencias

Esta librería viene con una anotación «built in» que es esencial en la programación orientada a objetos cómo lo es la capacidad de sobreescribir el comportamiento de la super clase. La anotación «$override» es un aspecto que modifica el comportamiento del método anotado. Concretamente esta anotación inserta un parámetro en el método que se corresponde con el método de la clase padre. Veamos el resultado del ejemplo anterior:

var manuel = new Person("Manuel", new Date(1982, 5, 10));
var pedro = new Programmer("Pedro", new Date(1987, 8, 17));

manuel.run(); //imprime > "Im running!"
pedro.run(); //imprime > "Im running! but... not as faster, coz im fat :/"

kaop nos permite definir cuando va a ejecutarse la anotación, antes del método, o después.

Si nuestra anotación se ejecuta antes, tendremos acceso a los parámetros de entrada y podremos modificarlos, insertar mas parámetros, transformarlos, etc. También podremos ejecutar código asíncrono, cómo llamadas AJAX, e insertar el resultado dentro de los métodos…

Si nuestra anotación se ejecuta después podremos añadir funcionalidades que no tengan impacto en el performance de la aplicación, o acceder al valor de retorno del método, etc.

En realidad estoy enumerando aquellas funcionalidades que me vienen en la cabeza, pero los límites están definidos únicamente por aquellos recursos a los que se tiene acceso dentro del contexto (scope) de la función de la anotación que son los siguientes:

annotations.add(function $save(index){ //$save es el nombre de la anotación que usaremos para `anotar` los métodos
  // hooks 
  // this.before(function(opts, next){ 
  // this.after(function(opts, next){ 
  this.after(function(opts, next){

    //Este método será ejecutado después de la ejecución del propio método anotado
 
    // stuff... 
 
    opts.args //contiene un array de los argumentos que recibe el método 
    opts.result //contiene el resultado retornado del método (no tendrá valor si la anotación se ejecuta en 'before')
    opts.scope //tiene la referencia al 'this' del método anotado
    opts.parentScope //te proporciona acceso al prototype o super clase 
    opts.method //es la función o 'método' que se ha anotado 
    opts.methodName //contiene el nombre del método anotado 
 
    // next() //es obligatorio llamar a esta función cuando finalice la lógica de la anotación
    // stuff... 
 
    //--------------------------------------------------------------
    // asynchronous example 
    myService.get("myEndPoint").success(function(){
      // stuff... 
      next() //cuando se completa la llamada ajax le decimos a la aplicación que esta anotación ha finalizado 
    })
  })
})

¿Que es next()? next es una función que debemos ejecutar cuando queramos ‘saltar’ a la siguiente anotación por orden: primero se ejecutan todas las anotaciones ‘before’, luego el método y después, todos los ‘after’.

Las anotaciones se ejecutan de forma secuencial y por orden, supongamos que tenemos el siguiente método:

{
  ...,
  save: ["$post: '\endpoint'", "$log", function(data, result){
    return result;
  }],
  ...
}

Tenemos un método llamado save con dos anotaciones (si, las anotaciones también pueden tener argumentos). Básicamente queremos que nuestra clase X, al llamar al método save -> X.save(data), primero se haga una llamada http a nuestra API rest, luego que imprima el resultado de la llamada en la consola de javascript y sólo después se ejecute el método de la clase, que en este caso sólo va a devolver el segundo parámetro de la función. Pero ese parámetro no ha sido introducido desde fuera, si no que ha sido la propia anotación ‘$post’ que ha añadido ese parámetro… hay que tener en cuenta que, ya que toda la ejecución del método save contiene, al menos, una llamada asíncrona tendrá poco sentido devolver un resultado en el método, puesto que este no podrá ser asignado o evaluado fuera de la función.

Ejemplos: https://github.com/ciroreed/kir/blob/master/src/common/customAnnotations.js

Conclusión. La programación orientada a aspectos permite diseñar aplicaciones grandes con un código funcional y con objetivos claros de manera organizada y ordenada. En el mundo web no existen muchas alternativas a la hora de implementar este patrón y es por eso que traté de reunir otras funcionalidades que en breves (…) estarán implementadas en todos los navegadores cómo lo son las clases (ES6) y anotaciones (ES7). Os animo a utilizar esta librería y conocer vuestros comentarios e impresiones.

Saludos!

Por ciroreed

Hola,

Vivo y trabajo en Alicante, pero nací en Alcoy en el 90. Considero Javascript mi especialidad, la programación mi hobby, y también mi trabajo.