1

POO en CRIAX-SDK

18Feb
en CRIAX-SDK, Herramientas, Javascript, SDK, Tutoriales, XUL

Prometido en el post anterior, aquí sí veremos código real:

  • Consola de la aplicación
  • POO

Consola de la aplicación

Una vez que la aplicación esta corriendo se pueden ver algunas métricas de la misma, desde la consola de la aplicación para ello ejecutamos el archiconocido toolchain/update. Una vez levantda la aplicación apretamos F6, se mostrará la consola en la parte de arriba a la derecha. Esta consola permite la entrada de algunos comandos y ejecución de código js.

Consola de la aplicación

Consola de la aplicación

Escribamos ahora sí, el primer hola mundo real, para ello nos adentramos en el directorio /source/class/helloworld/view/ allí encontraremos un archivo llamado View.js lo abrimos con nuestro editor favorito y en la línea 86 Enter y agregamos

this.info("Hola Mundo");

Bueno ya sabes lo que toca, actualizar (update). Cuando abramos la consola (F6) veremos la frase «Hola Mundo»:

Hola mundo en consola de aplicación

Hola mundo en consola de aplicación

Las demas cosas son métricas de carga de la aplicación, por ahí podemos averiguar la velocidad con la que carga, y en que se ejecuta completamente. En azul (info) veremos el «Hola mundo», directamente de la clase de donde se ejecuta.

Ahora juguemos con esa consolita, sí porque también se le pueden pasar comandos de js y ejecutar código de las clases del proyecto. Por ejemplo pasemos esto a la consola:

window.alert("Hola Mundo");
Mensaje de alert

Mensaje de alert

Sí, nos salió una ventanita de alerta. Si vamos a la consola del sistema podremos apreciar que allí tambien se escribio window.alert(«Hola Mundo»);. Recuerdas el botoncito que tiene la aplicación púlsalo, verás que en la consola saldrán 4 Application helloworld, esos son los distintos tipos de mensajes identificados por colores.

  • debug: fondo blanco
  • info: fondoazul
  • warn: fondoamarillo
  • error: fondo rojizo
Mensajes de la Consola

Mensajes de la Consola

Mensajes en la consola del sistema

Mensajes en la consola del sistema

Bueno pero aquí no termina todo, para acabar podemos navegar por los comandos tipados en la consola con las flechas de arriba y abajo. Si vamos a la consola y apretamos flecha arriba veremos como nos aparecer window.alert(«Hola Mundo»);. También podemos limpiar la consola apretando F1 (debemos marcar la consola) y ocultar la misma con F6.

POO en CRIAX-SDK

No me voy a poner a dar toda una retórica sobre los conceptos asociados a la Programación Orientada a Objetos eso lo dejaremos para el libro de CRIAX-SDK, aquí al grano.

Antes que nada dar las gracias a Enrique Place, por el excelente libro “PHP orientado a Objetos” bajo la licencia https://creativecommons.org/licenses/by-nc/3.0/, este es uno de los tanto geniales libros en los que me baso para crear este manual.

Aplicando el consejo de Enrique lo trataremos de hacer de la forma profesional, con tal de mejorar, aprender y evolucionar.

Ahora por favor, sé lo suficiente humilde como para consumir el conocimiento que aquí expongo sin interrumpir ni poner peros, ya verás que con el tiempo tú mismo buscarás y querrás saber más.

¿Qué es lo que intenta hacer entonces la Programación Orientada a Objetos (POO)?

Lisa y llanamente intentar simplificar la complejidad y tratar de representar de forma simple lo que vemos y manejamos todos los días, los objetos que nos rodean. “Lo bueno si es simple es 2 veces bueno”.

Un niño, cuando empieza a hablar, nos demuestra que ya entiende el concepto de “objetos”, empieza a nombrar esas “cosas”:

  • vaso
  • agua
  • papá
  • mamá
  • casa
  • guau guau (perro)

Todos estamos sumidos en un mundo que tiene reglas (sepamos o no que existen, nos afectan), como cuando tiramos un objeto y este cae (gravedad), cuando pisamos la cola de un gato y este llora. Nos vamos dando cuenta que generalmente no manipulamos los objetos directamente ni tenemos un completo control sobre ellos, muchas veces solo interactuamos con ellos.

Nos damos cuenta que cada uno tiene dentro una “programación” que le dice cómo reaccionar ante determinados estímulos o situaciones, y descubrimos luego que un gato reacciona distinto de otro gato (a pesar que ambos son gatos) y que no es lo mismo la reacción de un perro con respecto a la de un gato (a pesar que entre ellos existe una relación que los une como mamíferos).

Por lo tanto, así son los objetos: pueden ser de un tipo determinado (perro, gato), también pertenecer a una misma familia (mamíferos) y a su vez ser únicos (“el gato llamado Misu”).

Entonces, la Programación Orientada a Objetos no es más que eso, detectar los objetos existentes en nuestro contexto real y construirlos como si fuéramos “Dios”, dándoles un comportamiento para que estos sepan solos cómo reaccionar ante la interacción con otros objetos.

A esta actividad la llamaremos “diseño” y será cuando debamos decidir cómo serán y cómo se comportarán nuestros objetos ante la interacción con otros objetos.

Lo mas importante es detectar los objetos dentro de un contesto determinado. El código con el que se construyen los objetos es meramente circunstancial, una vez que tengamos claro el diseño conceptual, luego será seguir la receta con el lenguaje de turno (en este caso JS).

Un niño pequeño

Empecemos por el diálogo de un niño que recién empieza a hablar:

Micaela (hija de Enrique Place), de 5 años, dice: “mira el perro negro y blanco, se llama Tito, le toco la cabeza y mueve la cola, y si le doy de comer, al rato, hace caca”.

Claramente tenemos un objeto de tipo “Perro” con características bastante definidas.

Tenemos entonces:

  • “Un perro”, el “objeto” propiamente dicho.
  • “Es de color negro y blanco”, el color es un atributo del objeto “perro”
  • “Reacciona si le tocan la cabeza”, el comportamiento ante un estímulo externo.
  • “Mueve la cola”, tiene acciones.
  • “Come”, otras acciones relacionadas con su exterior/interior
  • “Hace caca”, tiene otras acciones que están relacionadas con su interior, y que posteriormente se exteriorizan de alguna forma.

También es importante destacar, un poco más sutil, que existe otro objeto en este escenario y se llama “Micaela”, y además existe (aunque no lo veamos) un contexto donde “viven” los objetos y que permite que se genere una interacción entre ambos.

El medio de comunicación

De alguna forma u otra, ambos objetos tienen cosas en común: existe un medio que les permite comunicarse, pero a su vez ellos tienen los elementos para generar ese “diálogo”, como así también existen “acciones” que son “internas” e “implícitas” de cada uno:

  • Aunque Micaela –y aún el perro- no lo entienda, ambos tienen internamente distintos mecanismos de digestión y ninguno controla el mecanismo del otro.
  • El perro, que sabe que cuando está nervioso mueve la cola, no logra entender del todo por qué si Micaela lo acaricia, esta también se mueve. Micaela sabe que si lo acaricia su cola se moverá.
  • Micaela tiene una mano y el perro una cabeza, Micaela tiene acceso a su cabeza, y la cabeza es accesible para que la mano pueda acariciarla.

Parece tonto y simple, pero así son los objetos, y en esos temas tenemos que pensar cuando diseñamos.

Cómo representar la estructura de los objetos en UML

Tratemos de representar lo que podemos interpretar del texto que está en un lenguaje natural y nos describe claramente los objetos y sus relaciones:

Micaela, de 5 años, dice: “mira el perro negro y blanco, se llama Tito, le toco la cabeza y mueve la cola, y si le doy de comer, al rato, hace caca”.

Si nos concentramos en el perro, tenemos que:

Atributos

  • Color
  • Nombre

Comportamiento

  • Se le puede tocar la cabeza
  • Mueve la cola
  • Puede comer
  • Sabe hacer sus necesidades

Y se desprende prestando atención a la lectura del texto que define nuestro contexto.

1) “Los Objetos tienen Atributos, Comportamientos y Estados”

Todos los objetos tienen “atributos”,”comportamientos” (“métodos”) y un “estado”. Este último no es más que la información que tienen los atributos en un momento dado.

2) Un perro se llamará “Tito” y otro “Ruffo”, pero el atributo es el mismo (“nombre”). Si mañana el perro cambia de nombre, lo que cambia es “su estado” y el mecanismo para cambiar el estado serán sus métodos (“cambiar el nombre”).

3) “La clase, un molde para construir objetos”

Este ejemplo es muy usado para tratar de transmitir el concepto que hay detrás. Podríamos decir que si “jugáramos a ser Dios”, primero definiríamos un diseño de cómo va a ser la criatura que queremos crear, haríamos un “molde” a partir de ese diseño, y posteriormente podríamos crear “vida” con similares características, pero que siempre serán “objetos únicos”.

Por ejemplo, si quiero crear “Personas” diría que todas tendrán un sexo (masculino / femenino), dos piernas, dos brazos, una cabeza y pelo sobre ella.

Por ejemplo, podría decir que los “Perros” también tendrían un sexo, pero ahora tendrían cuatro patas, una cabeza y pelo sobre todo su cuerpo.

Para ambos ejemplos ya cuento con dos moldes, el de Personas y el de Perros, por lo tanto ahora puedo crear a Micaela y a Tito, pero también podría crear a Martina y a Ruffo, por lo que tendríamos dos personas y dos perros, con características similares, pero que serían a su vez criaturas únicas, identificables y distinguibles entre las demás criaturas, aún entre las criaturas del mismo tipo (aunque se llamaran igual y tuvieran los mismos rasgos, serían solo parecidos).

4) “Los atributos y comportamientos pueden ser públicos o privados”

Existirá información y comportamientos que serán conocidos por otros objetos (“acceso público”) y esto permitirá que se pueda generar una interacción entre ellos. También existirá información y comportamientos que serán internos de cada objeto (“acceso privado”) y no serán conocidos por los demás objetos.

Un diseño posible para nuestro Perro podría ser:

Representacion de Objeto en UML

Representacion de Objeto en UML

Cómo se debería leer este diagrama:

  • Todo lo que se encuentre dentro de la representación “Perro” con letra normal es “interno y privado” (invisible para el exterior) y lo que se encuentre con letra cursiva será nuestra “interfaz” con el exterior, los “comportamientos públicos” del objeto que serán invocados por los “mensajes” enviados por otro objeto que quiere interactuar con nosotros.
  • Un objeto de tipo “Perro” tiene como atributos su “color” y su “nombre”
  • Dentro de los posibles comportamientos públicos de nuestro perro podrían ser “comer”, “recibir una caricia”, etc.
  • Dentro de los posibles comportamientos privados de nuestro perro podría ser “hacer la digestión”, que muy probablemente será activada a través de otros métodos (como “comer”).

Qué es lo que vería Micaela desde el exterior:

Lo que se ve desde el exterior de la clase

Lo que se ve desde el exterior de la clase

Solo podríamos interactuar con lo que es “público” del diseño, como así lo decidimos. Ahora la pregunta sería “¿Qué debería ser público y qué debería ser privado?”, bueno, intentemos usar el sentido común:

  • el perro necesita poder interactuar con su exterior de alguna forma, pero existirán “detalles” que no conciernen al exterior ni nos interesa que otro objeto pueda modificar a su antojo.

Por ejemplo, Micaela no tiene por qué saber cómo es el mecanismo de digestión de un perro, qué órganos entran en juego durante todo el proceso.

Lo que sí sucederá es que Micaela generará indirectamente que se active el mecanismo interno de digestión al darle de comer al Perro (“Micaela le dice al perro que coma”, lo que se debe traducir como “Micaela le envió un mensaje al perro”), pero esto ocurrirá sin que Micaela sepa que sucede, solo podrá apreciar sus resultados cuando el perro haga sus necesidades.

Ahora bien, ¿de quién es la responsabilidad de definir todas estas reglas?

Si, obviamente, la responsabilidad es nuestra, y un diseño será más débil o más robusto de acuerdo a cómo nosotros pensemos que deben reaccionar nuestros objetos a los mensajes que recibirán del exterior y cuanto oculten de sus propios detalles de implementación.

De la misma forma que a veces nos apoyamos en un dibujo para tratar de comprender y razonar un problema, muchas veces complejo, es fundamental contar con una “herramienta gráfica” (para armar “diagramas”) al menos al principio, que nos permita discutir y elaborar diseños sin tener que distraernos en los superfluos y cirscunstanciales detalles de la codificación según el lenguaje que necesitemos usar.

Diseñar Orientado a Objetos es independiente del lenguaje de programación, por lo tanto usaremos UML, un lenguaje “gráfico” independiente de la implementación.

Los diagramas UML son el medio y no el fin, sirven para simplificar notablemente las discusiones sobre “abstracciones” y mejoran la comunicación entre personas, ya sean desarrolladores como otros roles dentro de un mismo proyecto.

Para lo que a nosotros nos concierne, vamos a apoyarnos en los diagramas de clases y diagramas de paquetes.

Mi primer diagrama UML

Definamos un contexto para luego diseñarlo con UML:

“Una persona tiene nombre, apellido y fecha de nacimiento, cuando se le pregunta qué edad tiene, responde con su edad que calcula en base a la fecha de nacimiento”

Y el diagrama UML debería verse de la siguiente manera:

Clase Persona

Clase Persona

Para lograr este sencillo diagrama, utilizamos la herramienta jsUML que viene entre los vendors de CRIAX-SDK. Para levantarla ejecutamos el comando toolchain/jsuml. En otro post se explicará el uso de la misma.

Este diagrama se debe leer de la siguiente forma:

Clase por partes

Clase por partes

La Sección #1 es para definir el nombre de la clase (como explicamos al principio, usando CamelCase).

La Sección #2 es para definir los atributos de nuestra clase (CamelCase, pero a diferencia de las clases, inician con minúscula), la visibilidad de cada uno (el signo “-“ para privado, el signo “+” para público) y qué tipo de dato debería ser (no importa si el lenguaje lo soporta exactamente, recuerda que UML es independiente del lenguaje).

La Sección #3 es para definir los métodos de nuestra clase (CamelCase, igual que con los atributos), la visibilidad de cada uno, los parámetros que pueden recibir y si retornan o no alguna información (para ambos casos, especificando el tipo de dato).

Regla: “todos los atributos de una clase son por defecto no-públicos”.

Cómo se traduce en código

Vayamos por partes, empieza por arriba y sigue secuencialmente hacia abajo, y la traducción se hará muy simple y natural.

Sección #1 – nombre de la clase

Siguiendo la nomenclatura del estándar, deberíamos primero crear un archivo / fichero con el mismo nombre que la clase: Persona.js dentro del directorio del proyecto (source/class/helloworld/model/)

Posteriormente, todo se traduce en:

Declarar clase

Declarar clase


qx.Class.define("helloworld.model.Persona",
{
    extend: qx.core.Object,
});

Por defecto las clases normales extienden de qx.core.Object

Sección #2 – los atributos de la clase

Según el diagrama, tenemos 3 atributos, todos “privados” (signo “-“) y dos serán de tipo “String” (cadena de caracteres) y uno de tipo Date (fecha).

Aquí es donde tenemos que interpretar el diagrama y ajustarlo a nuestro lenguaje.

Atributos de clase

Atributos de clase

qx.Class.define("helloworld.model.Persona",
{
    extend: qx.core.Object,

    properties:
    {
        __nombre:{},
        __apellido:{},
        __fechaNacimiento:{},
    }
});

Siguiendo el estandar, usaremos el agregado “__” para todos los atributos que son “privados”. No olvidar, esto no es un problema que debe contemplar UML, ya es un problema del lenguaje de turno, en nuestro caso, js (CRIAX).

Pero como JS es un lenguaje de “tipado dinámico” no requiere definir inicialmente un tipo para sus variables, el tipo es definido dinámicamente cuando se hace una asignación de valor.

Lo máximo que podríamos hacer es:

qx.Class.define("helloworld.model.Persona",
{
    extend: qx.core.Object,

    properties:
    {
        __nombre:{ init:"" },
        __apellido:{ init:"" },
        __fechaNacimiento:{ init:"" }
    }
});

Al asignarle un valor por defecto de tipo String, su atributo sería de tipo String y cumpliríamos exactamente con el diagrama, a no ser por la fecha.

Si tuviéramos la necesidad de definir un Integer, simplemente asignaríamos un 0 a nuestro atributo, por ejemplo:

__edad: {init:0}

Sección #3 – los métodos de la clase

Finalmente, la traducción de los métodos. Aquí tenemos un método público (signo “+”), que no recibe parámetros (los paréntesis están vacíos) y luego de los “:” representan qué tipo de valor retornará (en este caso será un número entero que representará la edad).

Métodos de la clase

Métodos de la clase

El resultado en código sería:

qx.Class.define("helloworld.model.Persona",
{
    extend: qx.core.Object,

    properties:
    {
        __nombre:{ init:"" },
        __apellido:{ init:"" },
        __fechaNacimiento:{ init:"" }
    },

    members:
    {
        decirEdad : function(){

        }
    }
});

Como se puede observar los métodos de la clase se encuentran dentro de la sección de members.

El Constructor

Si tuviéramos la necesidad de documentar alguna particularidad del constructor, lo que generalmente se hace es agregar un método con el mismo nombre de la clase, lo que representaría “el constructor” de la clase, se utliza la palabra reservada construct, para el nombre de este metodo que va afuera de la sección de members.

Constructor de la clase

Constructor de la clase

qx.Class.define("helloworld.model.Persona",
{
    extend: qx.core.Object,

    properties:
    {
        __nombre:{ init:"" },
        __apellido:{ init:"" },
        __fechaNacimiento:{ init:"" }
    },

    construct: function(fechaNacimiento)
    {
        this.__fechaNacimiento = fechaNacimiento;
    },

    members:
    {
        decirEdad : function(){

        }
    }
});

Hasta aquí dentro del directorio /source/class/helloworld/model/, tenemos un archivo llamado Persona.js, y dentro debe estar el mismo código anterior.

Muy bien hasta aquí al fin se puede ver como se codifica en CRIAX. Un detalle dentro de 5 días cuando revisemos este código de nuevo y esto que significa, claro no somos adivinos así que proporcionemosnos una ayudaita comentariando el código.

/**
  * Clase Persona, la primera creada para CRIAX
  *
  * @class Persona
  * @public
  * @extends qx.core.Object
  * @author Lector
  * @namespace helloworld.model
  * @copyrigth
  * @license
  * @version 0.0.1
  *
  */

qx.Class.define("helloworld.model.Persona",
{
    extend : qx.core.Object,

    /**
     * @property
     */
    properties :
    {
    	/**
    	 * propiedad para el nombre
    	 *
    	 * @name __nombre
    	 * @private
    	 * @type String
    	 *
    	 */
    	__nombre : { init:"" },

    	/**
    	 * propiedad para el apellido
    	 *
    	 * @name __apellido
    	 * @private
    	 * @type String
    	 *
    	 */
    	__apellido : { init:"" },

    	/**
    	 * propiedad para la fecha de nacimiento
    	 *
    	 * @name __fechaNacimiento
    	 * @private
    	 * @type String
    	 *
    	 */
    	__fechaNacimiento : { init:"" }
    },

    /**
     * metodo de inicializacion de la clase
     *
     * @constructor
     * @public
     * @param fechaNacimiento {String}: fecha de nacimiento [00/00/0000]
     */

    construct : function(fechaNacimiento)
    {
        this.base(arguments);
        this.__fechaNacimiento = fechaNacimiento;
    },

   /**
     * @method
     */
    members :
    {
        /**
         *  metodo para calcular la edad
         *
         * @method decirEdad
         * @public
         * @return edad {Integer}: edad calculada a partir de la fecha de nacimiento
         *
         */

        decirEdad : function(){

        }
    }
});

Ahora pongamos un poquito de sazón y calculemos la edad realmente:

 /**
  * Clase Persona, la primera creada para CRIAX
  *
  * @class Persona
  * @public
  * @extends qx.core.Object
  * @author Lector
  * @namespace helloworld.model
  * @copyrigth
  * @license
  * @version 0.0.1
  *
  */

qx.Class.define("helloworld.model.Persona",
{
    extend : qx.core.Object,

    /**
     * @property
     */
    properties :
    {
    	/**
    	 * propiedad para el nombre
    	 *
    	 * @name __nombre
    	 * @private
    	 * @type String
    	 *
    	 */
    	__nombre : { init:"" },

    	/**
    	 * propiedad para el apellido
    	 *
    	 * @name __apellido
    	 * @private
    	 * @type String
    	 *
    	 */
    	__apellido : { init:"" },

    	/**
    	 * propiedad para la fecha de nacimiento
    	 *
    	 * @name __fechaNacimiento
    	 * @private
    	 * @type String
    	 *
    	 */
    	__fechaNacimiento : { init:"" }
    },

    /**
     * metodo de inicializacion de la clase
     *
     * @constructor
     * @public
     * @param fechaNacimiento {String}: fecha de nacimiento [00/00/0000]
     */

    construct : function(fechaNacimiento)
    {
        this.base(arguments);
        this.__fechaNacimiento = fechaNacimiento;
    },

   /**
     * @method
     */
    members :
    {
        /**
         *  metodo para calcular la edad
         *
         * @method decirEdad
         * @public
         * @return edad {Integer}: edad calculada a partir de la fecha de nacimiento
         *
         */

        decirEdad : function(){
            var edad = this.__calcularEdad();
            return edad;
        },

        /**
         *  metodo para calcular la edad
         *
         * @method __calcularEdad
         * @private
         * @return edad {Integer}: edad calculada a partir de la fecha de nacimiento
         *
         */

        __calcularEdad : function(){
            var fecha = new Date();
            var diaActual = fecha.getDate();
            var mesActual = fecha.getMonth()+1;
            var annoActual = fecha.getFullYear();
            var fechaPasada =  this.__fechaNacimiento.split("/");

            /*
             * si el mes es el mismo y el dia inferior aun no
             * ha cumplido annos le quitaremos un anno actual
             * */
            if((fechaPasada[1] == mesActual) && (fechaPasada[0] > diaActual)){
                annoActual = annoActual - 1;
            }

            /*
             * si el mes superior al actual tampoco habra
             * cumplido annos le quitamos un anno al actul
             * */
             if((fechaPasada[1] > mesActual)){
                annoActual = annoActual - 1;
            }

            var edad = annoActual - fechaPasada[2];
            return edad;
        }
    }
});

Está bien y cómo se usa esto ahora? Recuerdas la consolita, pues bien actualiza (toolchain/update) y abre consola (F6). En la consola primero crearemos un objeto de la clase persona:

var persona = new helloworld.model.Persona("23/12/1986");

y Enter, pero y esto que es me dió un error, pero si lo hice todo como esta en el manual!!!!

Creando objeto de la clase Persona en consola

Creando objeto de la clase Persona en consola

El problema es que CRIAX no es adivino, quieres crear un objeto de una clase que no se conoce pues no se ha cargado automáticamente, ni se hace referencia a ella desde otra clase, simplemente para CRIAX la clase Persona no existe. Para ello cargaremos esta clase automáticamente cuando inicie la aplicación. Esto te suena familiar, si? del post anterior.

Abre el archivo config.json del directorio raíz y editemos la autocarga de clases:


//init class
"helloworld.controller.Controller",
"helloworld.view.View",
"criax.domain.ModelFacade"

agreguemos esta clase persona nueva:


//init class
"helloworld.controller.Controller",
"helloworld.view.View",
"criax.domain.ModelFacade",
"helloworld.model.Persona"

Ahora sí, actualicemos y abramos consola y ejecutemos:

var persona = new helloworld.model.Persona("23/12/1986");
window.alert(persona.decirEdad());
Ejecución del método "decirEdad" de la clase Persona

Ejecución del método «decirEdad» de la clase Persona

Ufff! vamos que este post sí que estuvo largo y por supuesto no podía faltar la ya conocida Bonificación. En caso de que pierdas el proyecto helloworld y no quieras hacerlo todo desde el inicio estará publicado en el repo de actualizaciones la versión actualizada. Creas un standalone una vez creado y generado el proyecto entonces con el updater puedes actualizar y ver resultado.

Dirección del repo: https://developer.firefoxmania.uci.cu/common/dev_files/tools/criax/repo/

Bonificación

Aquí te muestro algunos elementos de como se verán los componentes visuales con los que irás desarrollando en CRIAX-SDK,  para verlos en todo su explendor poner navegador en fullscreen que los disfrutes.

Si te ha gustado el artículo dame un voto eh!!!

 

1 Comentario

  1. Erick dice:

    Con esto ya se complacen a los usuarios que querían ver código. De los temas, a pesar de que el modern se parezca más a una app desktop, me gusta más el Indigo. Para gusto los colores.

Dejar un comentario

¿Eres humano? Entonces resuelve esta operación: * Límite de tiempo se agote. Por favor, recargar el CAPTCHA por favor.