viernes, 31 de julio de 2015

Construir un módulo Joose JS

Module(name,function)

La función global Module(name,function) hace que crear un namespace para las clases, roles y prototipos sea muy fácil. El módulo automáticamente crea el namespace llamado "name". Las clases que son creadas dentro de la función "function" serán puestas automáticamente dentro del namespace del módulo. Esto reduce el riesgo de conflictos de nombres. Un módulo proporciona una buena forma de estructurar y organizar el código que compone un proyecto (múltiples módulos) o una librería (normalmente un único módulo).

Module("com.test.module", function (m) {
    Class("Test", {
        methods: { world: function () { return "hello" } }
    });
    Class("Test2", {
        methods: { world: function () { return "hello" } }
    })
})

En el ejemplo la clase Test será globalmente conocida bajo el nombre "com.test.module.Test" y así se reduce el riesgo de conflictos de nombres.

jueves, 30 de julio de 2015

Construir una clase Joose JS

La función global Class(name, properties) crea una clase con el nombre "name". La clase es inicializada usando sus propiedades "properties" de esta manera:
  • isa: Indica de qué clase hereda, o sea, su superclase
  • does: Aplica roles a la clase
  • has: Crea atributos para la clase
  • methods: Crea los métodos de instancia
  • classMethods: Crea los métodos de clase
Existen también modificadores de métodos: before, after, around, override, augment

isa

Class("Avion", {
 isa: Transporte
})

En el ejemplo Avion heredará todos los métodos de la clase Transporte. No hay Herencia Múltiple.

does

Class("Pintura", {
 does: Coloreable
})

Aplica roles, a veces llamado traits (algo similiar a mixins), a la clase. Para aplicar múltiples roles se le pasa un array de roles.

has

Class("Puerta",{
 has: {
  color: {
   is: "rw",
   init: "blanca"
  },
  material: {
   is: "ro",
   init: "madera"
  }
 }
}

En el ejemplo la clase Puerta tiene dos atributos: color y material. El atributo "color" es de lectura/escritura y es inicializado a "blanca". El atributo "material" es de sólo lectura y es inicializado a "madera". Joose genera los métodos de acceso a los atributos automáticamente, en este caso se llamarían: getColor(), setColor(), getMaterial(). No hay un método setMaterial() porque fue declarado de sólo lectura.

methods

methods: {
 clear: function(){
  this.setX(0);
  this.setY(0);
 },
 stringify: function(){
  return "" + this.getX() + "," + this.getY()
 }
}

En el ejemplo agregamos dos métodos a la clase, los métodos clear() y stringify(). El primero para limpiar los atributos de la clase y el segundo para devolver un string que representa los valores de sus atributos.

classMethods

classMethods: {
 makeNew: function(){
  return new Punto3D()
 }
}

Funciona como "methods" pero para generar métodos de clase en vez de métodos de instancia. Ahora podemos crear un Punto3D llamando a Punto3D.makeNew()

Modificadores de métodos


Los métodos declarados como "before" son llamados antes del método que sobreescriben. El valor de retorno es descartado.
before: {
 clear: function(){ alert("Antes: " + this.stringify()) }
}

Los métodos declarados como "after" son llamados después del método que sobreescriben. El valor de retorno es descartado.
after: {
 clear: function(){ alert("Después: " + this.stringify()) }
}

Métodos especiales


Los objetos Joose devuelven el string "a ClassName" cuando se llama al método stringify(), siendo ClassName el nombre de la clase a la que pertenece el objeto. Éste método se puede sobreescribir. Algunos engines Js utilizan el método toString(), por eso no se modifica directamente toString(), Joose utiliza stringify().
Existe el método initialize() que Joose automáticamente utiliza para inicializar una instancia como esta: var point = new Point({x:10, y: 10})
Se lo puede sobreescribir para cambiar ese comportamiento. O podría ser útil definir un modificador "after" para el método initialize().

miércoles, 29 de julio de 2015

Clase Punto3D en Javascript usando Joose

Javascript


En Javascript vamos a implementar una clase Punto3D que hereda de la clase Punto implementada antes aquí.
// Implementamos una función para herencia
function inherit(superClass, subClass) {
    for(var i in superClass.prototype) {
        subClass.prototype[i] = superClass.prototype[i]
    }
}
Test.StandardPoint3D = function (x, y, z) {
    this.x = x || 0
    this.y = y || 0
    this.z = z || 0
}
// Hacemos que Test.Standard sea la super clase de Test.StandardPoint3D
inherit(Test.StandardPoint, Test.StandardPoint3D)
// No podemos asignar un nuevo prototipo porque ya tenemos uno de la super clase
Test.StandardPoint3D.prototype.getZ = function () {
    return this.z
}
Test.StandardPoint3D.prototype.setZ = function (z) {
    this.z = z;
}
var superMethod = Test.StandardPoint3D.prototype.clear;
Test.StandardPoint3D.prototype.clear = function () {
    superMethod.apply(this);
    this.z = 0;
}


Joose


Ahora reimplementamos la clase Punto3D usando la librería Joose y vemos que el nuevo código es más compacto y legible porque se ocupa de lo que nos importa.
Module("Test", function (m) {
    Class("Point3D", {
        isa: m.Point,
        has: {
            z: {
                is: "rw",
                init: 0
            }
        },
        after: {
            clear: function () {
                this.setZ(0)
            }
        }
    })
})

domingo, 26 de julio de 2015

Ejemplo de código Joose

Descargamos la libreria joose.js de https://code.google.com/p/joose-js/downloads/list y creamos este pequeño ejemplo llamado runpoint.html:


<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
  <script type="text/javascript" src="./joose.js"></script>
  <title>Joose Point</title>

  <script type="text/javascript">
  Class("Punto", {
   has: {
    x: {is: "rw"},
    y: {is: "rw"}
   },
   methods: {
    clear: function () {
     this.setX(0);
     this.setY(0);
    }
   }
  });

  Class("Punto3D", {
   isa: Punto,
   has: {
    z: {is: "rw"}
   },
   after: {
    clear: function () {
     this.setZ(0);
    }
   }
  });
  </script>

 </head>
 <body>

  <script type="text/javascript">
  var point = new Punto3D();
  point.setX(10);
  point.setY(30);
  var y = point.getY();
  point.z = 1;
  console.log("Un punto: ");
  console.log(point);
  console.log("var y: " + y);
  point.clear();
  console.log("Coordenada z luego de clear: " + point.getZ());
  var point2 = new Punto3D({ x: 10, y: 20});
  console.log("Coordenada y de point2: " + point2.y);
  </script>

 </body>
</html>

La salida en la consola del navegador es algo como esto:



runpoint.html:41 Un punto:
runpoint.html:42 f {x: 10, y: 30, z: 1}
runpoint.html:43 var y: 30
runpoint.html:45 Coordenada z luego de clear: 0
runpoint.html:47 Coordenada y de point2: 20