Llevabamos juntos 4 años y nunca habiamos podido ganar el campeonato de futbol que organizabamos todos los años... Esta es mi historia en la escuela primaria. De primer grado a tercero perdimos todos los campeonatos contra los demas grupos del mismo grado. Pero el 4to grado recibimos un compañero nuevo. Sin siquiera nosotros pensarlo tomo la posicion que nadie queria: portero! Nunca lo habiamos visto jugar pero perder un año mas no iba a hacer que nos sintieramos mas mal de lo que ya nos sentiamos.
Sin embargo ese año gracias a sus habilidades verdaderamente asombrosas de portero hizo que ganaramos los 3 partidos que tuvimos que jugar y lograr ser los campeones del año. Desde ese momento hasta que terminamos la escuela en sexto grado (3 campeonatos seguidos), salimos campeones en cada torneo de futbol que organizabamos anualmente y jugando verdaderamente bien.
Esta historia tan simple nos marco a muchos de los que formamos parte de ese grupo de "perdedores" que por tres años perdieron todos los partidos que jugaban pero que cambio apenas llego el y con su aporte al equipo, nos puso a jugar mejor que todos.
El nos enseño varias cosas:
- A veces menospreciamos a la gente (muchas veces solo por ser nuevos) y pensamos que no tienen nada que aportar a nuestro equipo / grupo (de trabajo o lo que sea) y nos perdemos del aporte que esa persona pueda dar.
- A veces cuando un grupo esta junto mucho tiempo y las cosas no salen como queremos lo que ocupamos para tener exito es una persona nueva, con ganas de hacer las cosas bien, con nuevas ideas, con nuevas formas de hacer las cosas, un cambio que nos incentive a todos a hacer las cosas mejor.
- A veces hay que tomar sacrificios y hacer las cosas que nadie mas quiere hacer para que el equipo completo pueda tener exito.
Muchos equipos ocupan una persona que pueda hacer a los demas sentirse seguros y hacerlos mejores. Cuando consigas en tu equipo esa persona que logre dar eso a los demas miembros de tu equipo, estaras mas cerca que nunca de poder alcanzar el objetivo que querias.
Por supuesto: nunca menosprecies a nadie, ni siquiera al mas nuevo...
viernes, 8 de enero de 2010
domingo, 6 de diciembre de 2009
Patron Proxy en Flash
Recientemente me encontraba trabajando con Shared Objects (SO) en Flex para persistir ciertas propiedades en el cliente y empece a ver mas frecuentemente el uso de este codigo:
Inmediatamente decidi mover esto dentro de una clase donde tenia un SharedObject y desde donde simplemente invocaba un metodo y le pasaba 2 parametros, uno con el key y otro con el value para el key, terminando con algo asi:
Ya mi codigo empezo a verse con mucho menos lineas de codigo y tenia una clase reutilizable para interactuar con SO:
Definia esto al inicio de mi clase indicando cual shared object queria usar
var settings:SharedObjectWrapper = new SharedObjectWrapper(SharedObject().getLocal("/settings"));
Invocaba a mi clase asi:
settings.put("property", value);
Sin embargo, queria hacer algo todavia mas sencillo, mas que por funcionalidad por majaderia :) y me acorde de la clase Proxy que trae el Flash API.
Que es?
La clase Proxy que viene en el paquete flash.utils, es una clase "abstracta"(aunque no es verdad pues AS3 no permite esas clases), que quiere decir que sus metodos que usemos deben ser definidos a la hora de extender la clase y ademas no puede ser instanciada. Esta clase viene a reemplazar a Object.__resolve y Object.addPropert en AS2.
El Proxy nos permite agregar comportamiento a otras clases por medio de la definicion de ciertos metodos. Estos metodos se encargan de "escuchar" los llamados a las propiedades y servicios (funciones) en un objeto y responder a esos llamados para redireccionarlos dentro de la clase (como veremos mas abajo)
Algunos de los metodos principales definidos en la clase Proxy son:
callProperty(name:*, ... rest):*
Escucha el llamado de una funcion, los parametros son:
name -> nombre de la funcion
rest -> un Array de parametros
y retorna un valor de cualquier tipo
getProperty(name:*):*
Escucha el llamado a una propiedad de un objeto
name -> nombre de la propiedad
y retorna un valor de cualquier tipo
setProperty(name:*, value:*):void
Escucha el llamado a una propiedad de un objeto a la que se le quiera asignar un valor
name -> nombre de la propiedad
value -> valor a setear, de cualquier tipo
Que quiere decir?
Que extendiendo esta clase y algunos de los metodos definidos podemos hacer que nuestras clases que ya existan puedan tener un comportamiento adicional al que ya tenian, sobre todo si son clases hechas por otras personas o que no podamos modficar.
Vamos a ver un ejemplo, digamos que ocupamos tener un objeto dentro de nuestra clase en el cual guardaremos propiedades como un Hash. Esto lo podemos hacer facilmente haciendo esto:
Sin embargo nuestros requerimientos nos indican que debemos logguear un mensaje indicando que propiedad esta seindo invocada y a que hora. Ahora el ejemplo talvez no tenga mucho sentido, pero sigan leyendo...
Si hicieramos esto con el codigo anterior tendriamos que poner codigo adelante de nuestros llamados (o detras) con la informacion logueada. Tambien podriamos hacer una clase que se encargue de encerrar esa logica de logguear y asignar data en un metodo, o podemos usar el Proxy:
El metodo getProperty es invocado cada vez que alguna instancia de la clase MyProxy le sea invocada una propiedad, algo asi:
El metodo setProperty es invocado cada vez que se setea una propiedad de la instancia:
Y finalmente el metodo callProperty es invocado cuando hacemos esto:
Como pueden ver no se tiene que llamar a proxy.getProperty("color") o proxy.setProperty("color", "verde") o proxy.callProperty("iterate"), sino directamente a las propiedades o nombres de metodos que queremos tener. y el FP se encarga de invocar esos metodos en tiempo de ejecucion. El se encarga de hacer la logica necesaria y la magia la ejecuta el flash player que reconoce cual metodo invocar (getProperty, setProperty, callProperty o el que sea).
Esto es super util como mencione arriba para agregar comportamiento a clases que ya existen, pensando en un AOP mucho mucho mas sencillo pero igualmente efectivo.
Pues finalmente lo que hice con mi SharedObject fue poner un comportamiento parecido al de arriba y entonces logre tratar mi interaccion con el shared object que inicialmente necesitaba al menos 5 lineas, que despues baje a la invocacion de solo un metodo a poder hacer algo como esto:
Teoria:
Esto que acabamos de ver es el patron de dise~no Proxy, que como lo indica su nombre es una objeto que toma el lugar de otro. Este patron es usado a traves del Flash Api y Flex Framework en multiples lugares como el Loader donde tenemos acceso a propiedades (width, height) aunque no tengamos acceso al contenido al mismo instante. Tambien el RemoteObject en Flex hace uso de este patron cuando invocamos operaciones directamente del remote Object, por ejemplo: ro_user.loginUser(username,password); en este caso se llama remote proxy, pero es basicamente la misma idea solo que accedemos a datos remotos o externos.
La usabilidad de este patron es bastante amplia y muchas veces es muy despreciado, pero he de admitir que es una solucion muy elegante y sencilla para muchos problemas, ademas que el Flash API nos provee las herramientas necesarias para hacer este tipo de cosas de una manera muy sencilla.
var settings:SharedObject = SharedObject().getLocal("/settings");
try
{
settings.data["property"] = value;
settings.flush();
}catch(e:Error)
{
...
}
Inmediatamente decidi mover esto dentro de una clase donde tenia un SharedObject y desde donde simplemente invocaba un metodo y le pasaba 2 parametros, uno con el key y otro con el value para el key, terminando con algo asi:
public function put(key:String, value:*):void
{
try
{
_sharedObject.data[key] = value;
_sharedObject.flush();
}
catch(e:Error)
{
log.error("Error while persisting setting. " + e);
}
}
Ya mi codigo empezo a verse con mucho menos lineas de codigo y tenia una clase reutilizable para interactuar con SO:
Definia esto al inicio de mi clase indicando cual shared object queria usar
var settings:SharedObjectWrapper = new SharedObjectWrapper(SharedObject().getLocal("/settings"));
Invocaba a mi clase asi:
settings.put("property", value);
Sin embargo, queria hacer algo todavia mas sencillo, mas que por funcionalidad por majaderia :) y me acorde de la clase Proxy que trae el Flash API.
Que es?
La clase Proxy que viene en el paquete flash.utils, es una clase "abstracta"(aunque no es verdad pues AS3 no permite esas clases), que quiere decir que sus metodos que usemos deben ser definidos a la hora de extender la clase y ademas no puede ser instanciada. Esta clase viene a reemplazar a Object.__resolve y Object.addPropert en AS2.
El Proxy nos permite agregar comportamiento a otras clases por medio de la definicion de ciertos metodos. Estos metodos se encargan de "escuchar" los llamados a las propiedades y servicios (funciones) en un objeto y responder a esos llamados para redireccionarlos dentro de la clase (como veremos mas abajo)
Algunos de los metodos principales definidos en la clase Proxy son:
callProperty(name:*, ... rest):*
Escucha el llamado de una funcion, los parametros son:
name -> nombre de la funcion
rest -> un Array de parametros
y retorna un valor de cualquier tipo
getProperty(name:*):*
Escucha el llamado a una propiedad de un objeto
name -> nombre de la propiedad
y retorna un valor de cualquier tipo
setProperty(name:*, value:*):void
Escucha el llamado a una propiedad de un objeto a la que se le quiera asignar un valor
name -> nombre de la propiedad
value -> valor a setear, de cualquier tipo
Que quiere decir?
Que extendiendo esta clase y algunos de los metodos definidos podemos hacer que nuestras clases que ya existan puedan tener un comportamiento adicional al que ya tenian, sobre todo si son clases hechas por otras personas o que no podamos modficar.
Vamos a ver un ejemplo, digamos que ocupamos tener un objeto dentro de nuestra clase en el cual guardaremos propiedades como un Hash. Esto lo podemos hacer facilmente haciendo esto:
var hash:Object = new Object();
hash['prop'] = value;
trace(hash.prop);
Sin embargo nuestros requerimientos nos indican que debemos logguear un mensaje indicando que propiedad esta seindo invocada y a que hora. Ahora el ejemplo talvez no tenga mucho sentido, pero sigan leyendo...
Si hicieramos esto con el codigo anterior tendriamos que poner codigo adelante de nuestros llamados (o detras) con la informacion logueada. Tambien podriamos hacer una clase que se encargue de encerrar esa logica de logguear y asignar data en un metodo, o podemos usar el Proxy:
//Primero importemos el namespace flash_proxy que es necesario para hacer override de los metodos de la clase Proxy
import flash.utils.Proxy;
import flash.utils.flash_proxy;
use namespace flash_proxy;
public class class MyProxy extends Proxy {
private var data:Object = new Object();// Este objeto contendra nuestra informacion
//Retorna undefined si la propiedad no existe
override flash_proxy function getProperty(name:*):*
{
trace("Invocando propiedad: " + name);
return data[name];
}
override flash_proxy function setProperty(name:*, value:*):void
{
trace("Agregando propiedad: " + name + " con valor " + value);
data[name] = value;
}
override flash_proxy function callProperty(name:*, ... rest):*
{
//Aqui podemos llamar a otros metodos en esta clase o de otro objeto
//que tengamos referencia dentro de la misma
if(name == "iterate")
{
trace("Contenido del objeto: ");
for each(var val:* in data) {
trace(val);
}
}
}
}
El metodo getProperty es invocado cada vez que alguna instancia de la clase MyProxy le sea invocada una propiedad, algo asi:
var proxy:MyProxy = new MyProxy();
trace(proxy.color);//Getproperty es invocado
El metodo setProperty es invocado cada vez que se setea una propiedad de la instancia:
proxy.color = "verde";//SetProperty es invocado
Y finalmente el metodo callProperty es invocado cuando hacemos esto:
proxy.iterate;
Como pueden ver no se tiene que llamar a proxy.getProperty("color") o proxy.setProperty("color", "verde") o proxy.callProperty("iterate"), sino directamente a las propiedades o nombres de metodos que queremos tener. y el FP se encarga de invocar esos metodos en tiempo de ejecucion. El se encarga de hacer la logica necesaria y la magia la ejecuta el flash player que reconoce cual metodo invocar (getProperty, setProperty, callProperty o el que sea).
Esto es super util como mencione arriba para agregar comportamiento a clases que ya existen, pensando en un AOP mucho mucho mas sencillo pero igualmente efectivo.
Pues finalmente lo que hice con mi SharedObject fue poner un comportamiento parecido al de arriba y entonces logre tratar mi interaccion con el shared object que inicialmente necesitaba al menos 5 lineas, que despues baje a la invocacion de solo un metodo a poder hacer algo como esto:
settings.property = value; //dentro de este llamado esta el try/catch y el flush del SO.
Teoria:
Esto que acabamos de ver es el patron de dise~no Proxy, que como lo indica su nombre es una objeto que toma el lugar de otro. Este patron es usado a traves del Flash Api y Flex Framework en multiples lugares como el Loader donde tenemos acceso a propiedades (width, height) aunque no tengamos acceso al contenido al mismo instante. Tambien el RemoteObject en Flex hace uso de este patron cuando invocamos operaciones directamente del remote Object, por ejemplo: ro_user.loginUser(username,password); en este caso se llama remote proxy, pero es basicamente la misma idea solo que accedemos a datos remotos o externos.
La usabilidad de este patron es bastante amplia y muchas veces es muy despreciado, pero he de admitir que es una solucion muy elegante y sencilla para muchos problemas, ademas que el Flash API nos provee las herramientas necesarias para hacer este tipo de cosas de una manera muy sencilla.
Etiquetas:
Flash,
Flash Proxy,
Flex,
Patrones,
Patrones de diseno,
Proxy,
Proxy Pattern,
Shared Objects
lunes, 16 de noviembre de 2009
Pseudo Threads en Actionscript
Como mencione en un post anterior el Flash Player aunque internamente tiene un sistema de Threads o hilos que permite ejecutar multiples tareas al mismo tiempo, no provee a nosotros los desarrolladores la funcionalidad para utilizar threads nativamente. Esto hace por supuesto que aplicaciones nuestras donde se procesen muchos datos o con funciones o procesamiento muy pesado haga que nuestras aplicacion se congele o se pegue...
Como desarrolladores no tenemos otra forma de hacer que esto funcion sino es emulando la funcionalidad de los threads. Digo emulando porque no hay forma de hacerlo nativamente y se hace dividiendo el proceso pesado en multiples pedazos.
Acerca de esto se ha escrito en multiples lugares con contenido muy completo y excelentes soluciones a distintos niveles de complejidad.
Las librerias anteriores y muchas otras lo que hacen es partir el proceso grande en multiples pedazos que seran ejecutados en momentos distintos. Esto disminuye la carga de trabajo y hace que el proceso no se ejecute de un solo sin permitir al FP refrescarse y por lo tanto pegarse.
Sin embargo de las librerias anteriores, mi favorita es la de Grant Skinner, se llama "Chunker" pues parte los procesos en pedazos. Sin embargo basado en esa clase, decidi hacer unas modificaciones que aqui presento:
El unico requisito para esta clase, es que la funcion que queremos partir debe devolver un valor booleano: true si todavia hay data que procesar, false si ya termino de procesar la informacion.
A continuacion presento dos ejemplos, ambos cargan el mismo archivo(analytics de este blog); uno lo hace sin los pseudo-threads y otro con el pseudo-thread. Podran ver la diferencia como uno se pega y el otro no. Es un archivo grande (4mb) asi que tengan paciencia. Apenas se carga se activa el boton de "Start"
Notas: 1. Debe aparecer un spinner cuando se aprieta el boton de "start". 2. los controles de la pantalla deben ser accesbles mientras se procesan los datos.
Sin Thread.
Con Thread.
Al partir nuestra funcionalidad en multiples ejecuciones y no solo en una, ocupamos una forma de llegar a saber donde terminamos de procesar y desde donde debemos empezar. Para esto puedo sugerir el uso del patron Iterator. Este patron permite a una coleccion encapsular la manera en que se recorre la coleccion y guardar dentro de si misma cual fue el ultimo elemento procesado, sin tener que tener mantener un indice de la coleccion por fuera. El iterador se encarga de todo. Esto es bueno ya que al partir nuestro proceso ocupamos mantener una referencia al dato que estamos procesando sin tener que crear otras variables.
Aqui presento las siguientes clases para tener nuestro iterator.
Interfaz Base:
Implementacion basica:
La ventaja de tener una interfaz definiendo el iterador, es que podemos crear nuestras propias implementaciones del iterador de acuerdo a nuestras necesidades.
Ya con nuestro iterador cargado con la coleccion de datos que tenemos que procesar, procedemos a utilizar nuestro thread.
El codigo de las aplicaciones de este ejemplo esta aqui.
Como desarrolladores no tenemos otra forma de hacer que esto funcion sino es emulando la funcionalidad de los threads. Digo emulando porque no hay forma de hacerlo nativamente y se hace dividiendo el proceso pesado en multiples pedazos.
Acerca de esto se ha escrito en multiples lugares con contenido muy completo y excelentes soluciones a distintos niveles de complejidad.
Las librerias anteriores y muchas otras lo que hacen es partir el proceso grande en multiples pedazos que seran ejecutados en momentos distintos. Esto disminuye la carga de trabajo y hace que el proceso no se ejecute de un solo sin permitir al FP refrescarse y por lo tanto pegarse.
Sin embargo de las librerias anteriores, mi favorita es la de Grant Skinner, se llama "Chunker" pues parte los procesos en pedazos. Sin embargo basado en esa clase, decidi hacer unas modificaciones que aqui presento:
package com.grayscale.util.threads
{
import flash.display.Shape;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.utils.getTimer;
/**
* Evento despachado cuando se concluye el proceso.
*/
[Event (type="flash.events.Event", name="complete")]
/**
* Esta clase es una implementacion simple de un pseudo thread en AS3.
*Esta basada en la clase de Grant Skinner aunque con algunas modificaciones.
* http://www.gskinner.com/libraries/Chunker.zip
*/
public class SimpleThread extends EventDispatcher
{
//Nos permite tener acceso al evento ENTER_FRAME que sera el que lleve el
//paso de cada cuando se ejecutaran nuestros procesos.
private var shape:Shape = new Shape();
//Estado del hilo
private var _paused:Boolean = false;
//Cada cuanto se ejecutara
private var _execTime:uint;
//Funcion a ejecutar
private var _func:Function;
//Argumentos a nuestra funcion. Es un array de elementos que pasariamos a nuestra funcion
private var _args:Array;
/**
* Constructor.
*
* @param execTime Cada cuanto se ejecutara nuestra funcion
* @param func funcion a ejecutar
* @param args parametro opcional con los valores a pasarse en la funcion
*/
public function SimpleThread(execTime:int, func:Function, args:Array=null)
{
_execTime = execTime;
_func = func;
_args = args;
pause = false;
}
/**
* Para el thread o lo arranca
*/
public function set pause(value:Boolean):void
{
_paused = value;
if(_paused) shape.removeEventListener(Event.ENTER_FRAME, run);
else shape.addEventListener(Event.ENTER_FRAME, run);
}
/**
* Metodo que ejecuta nuestra funcion
*/
private function run(event:Event):void
{
var initialTime:int = getTimer();//registra el tiempo donde inicia este iteracion
while(!_paused && (getTimer() < _execTime+initialTime ))//revisa si no hemos excedido la cantidad de tiempo del inicio
{
//llama nuestra funcion y esta tiene que regresar un valor booleano. Si este es falso detiene el thread sino continua.
var res:Boolean = _func.apply(this, _args) as Boolean
if(!res)//ya se termina de ejecutar nuestra funcion, pausamos el thread y despachamos evento avisando que se concluyo
{
dispatchEvent(new Event(Event.COMPLETE));
pause = true;
}
}
}
}
}
El unico requisito para esta clase, es que la funcion que queremos partir debe devolver un valor booleano: true si todavia hay data que procesar, false si ya termino de procesar la informacion.
A continuacion presento dos ejemplos, ambos cargan el mismo archivo(analytics de este blog); uno lo hace sin los pseudo-threads y otro con el pseudo-thread. Podran ver la diferencia como uno se pega y el otro no. Es un archivo grande (4mb) asi que tengan paciencia. Apenas se carga se activa el boton de "Start"
Notas: 1. Debe aparecer un spinner cuando se aprieta el boton de "start". 2. los controles de la pantalla deben ser accesbles mientras se procesan los datos.
Sin Thread.
Con Thread.
Al partir nuestra funcionalidad en multiples ejecuciones y no solo en una, ocupamos una forma de llegar a saber donde terminamos de procesar y desde donde debemos empezar. Para esto puedo sugerir el uso del patron Iterator. Este patron permite a una coleccion encapsular la manera en que se recorre la coleccion y guardar dentro de si misma cual fue el ultimo elemento procesado, sin tener que tener mantener un indice de la coleccion por fuera. El iterador se encarga de todo. Esto es bueno ya que al partir nuestro proceso ocupamos mantener una referencia al dato que estamos procesando sin tener que crear otras variables.
Aqui presento las siguientes clases para tener nuestro iterator.
Interfaz Base:
package com.grayscale.util.iterator
{
/**
* Esta interfaz define los metodos basicos que debera contener un iterador en nuestra coleccion
* @author Ivan Ramirez - ivan.ramirez@gmail.com
*/
public interface IIterator
{
/**
* Devuelve "true" si tenemos mas valores que recorrer dentro de nuestra coleccion
*/
function hasNext():Boolean;
/**
* Devuelve el proximo valor en nuestro iterador
* @returns el siguiente objeto en la coleccion.
*/
function next():*;
/**
* Nos permite reiniciar el recorrido de nuestra coleccion
*/
function reset():void;
}
}
Implementacion basica:
package com.grayscale.util.iterator
{
/**
* Implementacion basica de nuestro iterator para el manejo de arrays. Podemos implementar esta interfaz
* en otras clases para hacer iteradores con Strings o de XML, etc.
* @author Ivan Ramirez - ivan.ramirez@gmail.com
*/
public class ArrayIterator implements IIterator
{
//Coleccion base
private var collection:Array = new Array();
private var index:int;
public function ArrayIterator(values:Array)
{
collection = values
}
public function hasNext():Boolean
{
return index < collection.length;
}
public function next():*
{
return collection[index++];
}
public function reset():void
{
index = 0;
}
}
}
La ventaja de tener una interfaz definiendo el iterador, es que podemos crear nuestras propias implementaciones del iterador de acuerdo a nuestras necesidades.
Ya con nuestro iterador cargado con la coleccion de datos que tenemos que procesar, procedemos a utilizar nuestro thread.
El codigo de las aplicaciones de este ejemplo esta aqui.
Etiquetas:
Actionscript3,
Flash,
Flex,
Iterator,
Patrones,
Patrones de diseno,
Performance,
Rendimiento,
Threads
domingo, 15 de noviembre de 2009
Como actualiza la pantalla el Flash Player...
El Flash Player(FP) es la maquina virtual que nos permite ejecutar nuestras aplicaciones hechas en Flash, Flex o Actionscript Puro (incluso ejecuta codigo en otros lenguajes cuyo bytecode es "traducido" a un lenguaje que el player entienda, pero eso es para otro post)
Una de los principales usos que se hacen del FP es la parte grafica que ha provisto por muchisimo tiempo excelentes herramientas para desarrolladores y dise~nadores para crear contenido totalmente fuera de lo normal. De hecho podriamos pensar que lo que ha hecho grande y famoso a la plataforma flash es la capacidad de crear aplicaciones que brindan al usuario excelentes experiencias.
Pero... esas excelentes ideas y deseos de buenas experiencias pueden verse truncados si nuestras aplicaciones no estan haciendo uso correcto del FP, sobre todo en la forma en que este maneja la actualizacion de la pantalla.
Ejecucion:
* Definicion: Frame Rate: es la cantidad maxima de veces que por segundo el FP revisara si hay cambios en nuestra aplicacion que se tengan que dibujar en la pantalla. Esto no significa que si tenemos un framerate de 24 fps(frames per second) es que el FP vaya a revisar si hay cambios, 24 veces en un segundo. Esto quiere decir solamente que si todo sale bien, si nada del codigo que uno como desarrollador escribio demora mucho tiempo, el intentara revisar esa cantidad de veces, pero a veces no sera posible.
- Nuestra aplicacion es cargada dentro del FP y es agregada al stage.
- FP ejecuta el codigo inicial de nuestro Frame.
- FP ejecuta algun event listener que sea despachado.
- FP ejecuta el proceso de rendereo (o actualizacion de la pantalla), que basicamente revisa si el codigo ejecutado previamente realizo algun cambio en la parte grafica que deba ser dibujado en la pantalla. Cuando se hace ese chequeo se despacha el evento Event.ENTER_FRAME.
- Si hay cambios, actualiza la pantalla.
- FP espera hasta que se vuelva a ejecutar el chequeo. Si algun event listener es ejecutado (eventos del mouse o del teclado, etc) los ejecuta.
- Chequea si hay cambios, si los hay actualiza la pantalla.
- Repitir.
Conclusiones importantes:
- Este ciclo de chequeo se repite infinitamente durante la ejecucion de nuestra aplicacion, sea que tengamos 1 o tengamos miles de Frames, el FP solo le importa saber si hay cambios que se tengan que reflejar en la pantalla y el gustosamente se encargara de chequearlo. Cambios pueden ser: dibujar algo (graphics.drawLine,etc), mover el X o Y o el width o height de nuestros componentes o los componentes dentro de nuestra aplicacion, cambiar el color de algo, etc.
- Los cambios que hagamos dentro de una funcion a nuestros componentes visuales no se reflejaran inmediatamente en pantalla (aunque a veces parezca que si), sino hasta que nuestro codigo termine de ejecutarse y el cambio proceso de rendereo se ejecute. Por ejemplo si tenemos algo como: comp.width = 100; comp.height = 200; dentro de un metodo, esto NO quiere decir que FP vayaa ejecutar nuestro codigo asi:
1. Cambie el width.
2. Dibuje la pantalla con el nuevo width.
3. Cambie height.
4. Redibuje la pantalla con nuevo height
El FP ejecutara el redibujado de la pantalla hasta que nuestro codigo se termine de ejecutar.
- Aunque nuestras aplicaciones no tengan muchos frames (2 en el caso de Flex) o solo 1 el evento ENTER_FRAME se va a despachar multiples veces porque el FP va a revisar si hay en cambios en la pntalla que deba actualizar. De ahi que el evento ENTER_FRAME sea utilizado multiples veces para ejecutar animaciones o procesos que deben ejecutarse infinitamente dentro de un mismo frame. Essto quiere decir que si tenemos un proceso que debe ejecutarse infinitamente, no debemos meterlo dentro de un ciclo infinito, digamos while(true), sino dentro de este event listener.
- El ciclo de rendereo, es realizado automaticamente por el FP.
- Si se esta ejecutando codigo de la aplicacion, el proceso de rendereo de la pantalla no se va a ejecutar y viceversa. Estos dos son exclusivos, no se puede chequear mientras se ejecuta codigo (funciones o event handlers) y no se puede ejecutar ningun metodo o event handler si se esta haciendo el chequeo. Por ejemplo, si nuestro frame rate es de 10 fps quiere decir que el FP va a revisar en teoria cada 100ms (1000 ms = 1 segundo, 1000 / 10 = 100 :) ) si hay que hacer una actualizacion. Pero como mencione arriba el framerate no necesariamente se va a ejecutar al mismo tiempo siempre. Si tenemos una rutina que recorre un Array con 1000 objetos por dentro y a cada objeto se le ejecuta algun metodo, una validacion y se le setea algunos valores, puede ser que esa funcion dure mas de los 100ms que esperaria el FP para revisar si hay cambios. Esto quiere decir que si la funcion demora 900ms, el FP no revisara si habra cambios hasta el siguiente segundo, no cuando se cumplan 200ms desde el primer chequeo.
- Si tenemos rutinas de codigo muy grandes, muy pesadas nuestra pantalla podria llegar a "congelarse" o verse "chunky", como pegada. Esto es porque como se expuso arriba la actualizacion de la pantalla no se va a realizar hasta que se termine de ejecutar nuestor codigo. Entre mas duren nuestras rutinas mas va a durar en refrescarse nuestra pantalla y peor puede ser la experiencia del usuario.
- Hay un tiempo maximo de ejecucion de codigo que por default es de 15segs. Si por ejemplo tenemos una clase y hacemos un ciclo while(true) en el constructor, podemos esperar 15 segs y ver como nuestra aplicacion va a mostrarnos un error. Esto es porque el tiempo maximo de ejecucion de una funcion fue excedida.
Formas de Optimizacion:
- Si tenemos rutinas muy grandes y que demoran mucho tiempo, lo mejor seria dividir esas rutina en varias para ejecutarse en distintos tiempos, algo como lo que se puede hacer con Threads en otros lenguajes. Recordemos que aunque el FP maneja Threads internamente, nosotros como desarrolladores no tenemos acceso a ellos y solamente podemos emularlos.
- Si utilizamos Flex, hagamos uso de los componentes creados en el framework que en su mayoria estan optimizados para este ciclo de actualizacion de pantalla. Si vamos a hacer nuestros propios componentes, sigamos las reglas y hagamos un override de los metodos sugeridos por Adobe (esto para FLEX 3) como commitProperites y updateDisplayList entre otros. Ambos metodos mencionados antes tienen su razon de ser una funcion especifica para hacer que se ejecuten las cosas de una mejor manera.
- Si no utilizamos Flex, podemos hacer uso del evento Event.RENDER. Este metodo es despachado por el FP exactamente antes de dibujar los cambios en la pantalla. Podemos recibir este evento haciendo un addEventListener normal, pero ademas de eso tenemos que llamar a stage.invalidate(). Esto forzara al evento a ser despachado. Ahora en que nos ayuda este evento? Pues al ser despachado justamente antes de ser actualizada nuestra pantalla, podemos revisar en nuestro listener, si nuestra clase tiene algun cambio en la pantalla. Si lo tiene, llama a la funcion que dibuje o haga los cambios que se quieran hacer en pantalla. Si no hay cambios simplemente no hacemos nada. Eso nos permite forzar al FP redibujar lo que cambio SOLAMENTE cuando haya cambios que de verdad alteren nuestra pantalla y forzar a que la actualizacion de la pantalla se haga solamente hasta que se tenga que hacer. Podemos hacer mas uso de esto, si ponemos setters en nuestros componentes que se encargue de registra que hay cambios en nuestro objeto en un flag booleano. En lugar de llamar dentro del setter al metodo que se encarga de dibujar nuestros graficos o mover nuestros componentes cada vez que se cambia una variable. El event handler del metodo renderer, consultara si nuestro flag esta activado y si lo esta ahi llamara a nuestro metodo. Asi en lugar de llamar el metodo que dibuja nuestra pantalla cada vez que se cambia una variable, este se ejecutara una sola vez y exactamente antes de que se ejecute el redibujado de la pantalla. Esto es muy parecido al proceso de optimizacion que tienen los componentes en Flex.
Otros recursos:
El excelente libro de Colin Moock, Essential Actionscript 3.0, especialmente el capitulo 23
Racetrack: Excelente analisis de como funciona el proceso de rendereo en el Flash Player.
Una de los principales usos que se hacen del FP es la parte grafica que ha provisto por muchisimo tiempo excelentes herramientas para desarrolladores y dise~nadores para crear contenido totalmente fuera de lo normal. De hecho podriamos pensar que lo que ha hecho grande y famoso a la plataforma flash es la capacidad de crear aplicaciones que brindan al usuario excelentes experiencias.
Pero... esas excelentes ideas y deseos de buenas experiencias pueden verse truncados si nuestras aplicaciones no estan haciendo uso correcto del FP, sobre todo en la forma en que este maneja la actualizacion de la pantalla.
Ejecucion:
* Definicion: Frame Rate: es la cantidad maxima de veces que por segundo el FP revisara si hay cambios en nuestra aplicacion que se tengan que dibujar en la pantalla. Esto no significa que si tenemos un framerate de 24 fps(frames per second) es que el FP vaya a revisar si hay cambios, 24 veces en un segundo. Esto quiere decir solamente que si todo sale bien, si nada del codigo que uno como desarrollador escribio demora mucho tiempo, el intentara revisar esa cantidad de veces, pero a veces no sera posible.
- Nuestra aplicacion es cargada dentro del FP y es agregada al stage.
- FP ejecuta el codigo inicial de nuestro Frame.
- FP ejecuta algun event listener que sea despachado.
- FP ejecuta el proceso de rendereo (o actualizacion de la pantalla), que basicamente revisa si el codigo ejecutado previamente realizo algun cambio en la parte grafica que deba ser dibujado en la pantalla. Cuando se hace ese chequeo se despacha el evento Event.ENTER_FRAME.
- Si hay cambios, actualiza la pantalla.
- FP espera hasta que se vuelva a ejecutar el chequeo. Si algun event listener es ejecutado (eventos del mouse o del teclado, etc) los ejecuta.
- Chequea si hay cambios, si los hay actualiza la pantalla.
- Repitir.
Conclusiones importantes:
- Este ciclo de chequeo se repite infinitamente durante la ejecucion de nuestra aplicacion, sea que tengamos 1 o tengamos miles de Frames, el FP solo le importa saber si hay cambios que se tengan que reflejar en la pantalla y el gustosamente se encargara de chequearlo. Cambios pueden ser: dibujar algo (graphics.drawLine,etc), mover el X o Y o el width o height de nuestros componentes o los componentes dentro de nuestra aplicacion, cambiar el color de algo, etc.
- Los cambios que hagamos dentro de una funcion a nuestros componentes visuales no se reflejaran inmediatamente en pantalla (aunque a veces parezca que si), sino hasta que nuestro codigo termine de ejecutarse y el cambio proceso de rendereo se ejecute. Por ejemplo si tenemos algo como: comp.width = 100; comp.height = 200; dentro de un metodo, esto NO quiere decir que FP vayaa ejecutar nuestro codigo asi:
1. Cambie el width.
2. Dibuje la pantalla con el nuevo width.
3. Cambie height.
4. Redibuje la pantalla con nuevo height
El FP ejecutara el redibujado de la pantalla hasta que nuestro codigo se termine de ejecutar.
- Aunque nuestras aplicaciones no tengan muchos frames (2 en el caso de Flex) o solo 1 el evento ENTER_FRAME se va a despachar multiples veces porque el FP va a revisar si hay en cambios en la pntalla que deba actualizar. De ahi que el evento ENTER_FRAME sea utilizado multiples veces para ejecutar animaciones o procesos que deben ejecutarse infinitamente dentro de un mismo frame. Essto quiere decir que si tenemos un proceso que debe ejecutarse infinitamente, no debemos meterlo dentro de un ciclo infinito, digamos while(true), sino dentro de este event listener.
- El ciclo de rendereo, es realizado automaticamente por el FP.
- Si se esta ejecutando codigo de la aplicacion, el proceso de rendereo de la pantalla no se va a ejecutar y viceversa. Estos dos son exclusivos, no se puede chequear mientras se ejecuta codigo (funciones o event handlers) y no se puede ejecutar ningun metodo o event handler si se esta haciendo el chequeo. Por ejemplo, si nuestro frame rate es de 10 fps quiere decir que el FP va a revisar en teoria cada 100ms (1000 ms = 1 segundo, 1000 / 10 = 100 :) ) si hay que hacer una actualizacion. Pero como mencione arriba el framerate no necesariamente se va a ejecutar al mismo tiempo siempre. Si tenemos una rutina que recorre un Array con 1000 objetos por dentro y a cada objeto se le ejecuta algun metodo, una validacion y se le setea algunos valores, puede ser que esa funcion dure mas de los 100ms que esperaria el FP para revisar si hay cambios. Esto quiere decir que si la funcion demora 900ms, el FP no revisara si habra cambios hasta el siguiente segundo, no cuando se cumplan 200ms desde el primer chequeo.
- Si tenemos rutinas de codigo muy grandes, muy pesadas nuestra pantalla podria llegar a "congelarse" o verse "chunky", como pegada. Esto es porque como se expuso arriba la actualizacion de la pantalla no se va a realizar hasta que se termine de ejecutar nuestor codigo. Entre mas duren nuestras rutinas mas va a durar en refrescarse nuestra pantalla y peor puede ser la experiencia del usuario.
- Hay un tiempo maximo de ejecucion de codigo que por default es de 15segs. Si por ejemplo tenemos una clase y hacemos un ciclo while(true) en el constructor, podemos esperar 15 segs y ver como nuestra aplicacion va a mostrarnos un error. Esto es porque el tiempo maximo de ejecucion de una funcion fue excedida.
Formas de Optimizacion:
- Si tenemos rutinas muy grandes y que demoran mucho tiempo, lo mejor seria dividir esas rutina en varias para ejecutarse en distintos tiempos, algo como lo que se puede hacer con Threads en otros lenguajes. Recordemos que aunque el FP maneja Threads internamente, nosotros como desarrolladores no tenemos acceso a ellos y solamente podemos emularlos.
- Si utilizamos Flex, hagamos uso de los componentes creados en el framework que en su mayoria estan optimizados para este ciclo de actualizacion de pantalla. Si vamos a hacer nuestros propios componentes, sigamos las reglas y hagamos un override de los metodos sugeridos por Adobe (esto para FLEX 3) como commitProperites y updateDisplayList entre otros. Ambos metodos mencionados antes tienen su razon de ser una funcion especifica para hacer que se ejecuten las cosas de una mejor manera.
- Si no utilizamos Flex, podemos hacer uso del evento Event.RENDER. Este metodo es despachado por el FP exactamente antes de dibujar los cambios en la pantalla. Podemos recibir este evento haciendo un addEventListener normal, pero ademas de eso tenemos que llamar a stage.invalidate(). Esto forzara al evento a ser despachado. Ahora en que nos ayuda este evento? Pues al ser despachado justamente antes de ser actualizada nuestra pantalla, podemos revisar en nuestro listener, si nuestra clase tiene algun cambio en la pantalla. Si lo tiene, llama a la funcion que dibuje o haga los cambios que se quieran hacer en pantalla. Si no hay cambios simplemente no hacemos nada. Eso nos permite forzar al FP redibujar lo que cambio SOLAMENTE cuando haya cambios que de verdad alteren nuestra pantalla y forzar a que la actualizacion de la pantalla se haga solamente hasta que se tenga que hacer. Podemos hacer mas uso de esto, si ponemos setters en nuestros componentes que se encargue de registra que hay cambios en nuestro objeto en un flag booleano. En lugar de llamar dentro del setter al metodo que se encarga de dibujar nuestros graficos o mover nuestros componentes cada vez que se cambia una variable. El event handler del metodo renderer, consultara si nuestro flag esta activado y si lo esta ahi llamara a nuestro metodo. Asi en lugar de llamar el metodo que dibuja nuestra pantalla cada vez que se cambia una variable, este se ejecutara una sola vez y exactamente antes de que se ejecute el redibujado de la pantalla. Esto es muy parecido al proceso de optimizacion que tienen los componentes en Flex.
Otros recursos:
El excelente libro de Colin Moock, Essential Actionscript 3.0, especialmente el capitulo 23
Racetrack: Excelente analisis de como funciona el proceso de rendereo en el Flash Player.
Etiquetas:
AS3,
ENTER_FRAME,
Flash,
Flash Player,
Flex,
Render
domingo, 8 de noviembre de 2009
Eval function en Actionscript
Recordando mis tiempos de programador en Java, entre recientemente al site de una herramienta llamada Bean Shell que use hace varios a~nos... La herramienta sencillamente era un interprete de codigo en Java (y en otros lenguajes pues interpretaba Python y combinacion entre Python y Java), en tiempo de ejecucion, es decir sin tener que compilar un archivo .class y generar el bytecode para que sea ejecutado por el virtual machine...
Nosotros utilizabamos de esta funcionalidad para una herramienta administrativa, donde ejecutabamos scripts en un pseudolenguaje para consultar multiples y distintos servidores de informacion. Esto nos permitia tener multiples consultas dinamicas sin tener que recompilar la aplicacion principal o que los usuarios finales tuvieran que aprender a programar en algun lenguaje extra~no para ellos o usar SQL o algo por el estilo.
Viendo esto, me intereso encontrar algo parecido en Flash/Flex. La funcion Eval que esta disponible en multiples lenguajes, incluyendo Javascript, tiene una funcionalidad algo parecida, nos recibe de parametro una cadena de caracteres donde se tiene codigo que sera interpretado (y ejecutado) por esa funcion. En Flash era comun encontrar esta funcion (o algo parecido) en ActionScript 2 pero fue eliminada en la version 3.
Sin embargo haciendo uso del trabajo hecho en Tamarin Project de Mozilla, donde se tuvo acceso a parte del codigo del AVM2 osea el nuevo Action Virtual Machine que corre en el Flash Player 9.x y mas versiones mas recientes. Tamarin nos permite ver como funciona internamente el virtual machine, como se ejecuta el codigo, como se procesa, etc. Es algo bastante interesante y avanzado a la vez y permitiria la optimizacion de aplicaciones en la plataforma flash, incluso hay proyectos donde se promueve el uso de AOP o aplicaciones que convierten codigo de C# o Java a codigo ejecutable por el flash player... todo con la informacion que provee este proyecto; pero bueno, esto seran tema de otros posts.
Como decia anteriormente - antes de desviarme diciendo cuan impresionante es el proyecto - haciendo uso del proyecto Tamarin, "Metal Hurlant" (creador de multiples librerias open source como AS3Cripto) creo una libreria que se comporta como la funcion Eval, interpretando codigo de Actionscript en tiempo de ejecucion. Pueden acceder a un demo desde aqui.
Solo puedo decir que este es un trabajo brillante y que tomando las medidas de seguridad pertinentes (para que no se inyecte codigo maligno en tiempo de ejecucion entre otras) y no abusando de la misma por cuestiones de rendimiento (este proceso demanda algo de recursos) puede llegar a ser de mucha utilidad. La creacion de aplicaciones mas dinamicas, mas inteligentes en tiempo de ejecucion, etc. nos permitira ofrecer mejores aplicaciones a nuestros usuarios y a la vez creando aplicaciones menos complejas y flexibles... nuevamente sin abusar de esto...
Disfruten...
Nosotros utilizabamos de esta funcionalidad para una herramienta administrativa, donde ejecutabamos scripts en un pseudolenguaje para consultar multiples y distintos servidores de informacion. Esto nos permitia tener multiples consultas dinamicas sin tener que recompilar la aplicacion principal o que los usuarios finales tuvieran que aprender a programar en algun lenguaje extra~no para ellos o usar SQL o algo por el estilo.
Viendo esto, me intereso encontrar algo parecido en Flash/Flex. La funcion Eval que esta disponible en multiples lenguajes, incluyendo Javascript, tiene una funcionalidad algo parecida, nos recibe de parametro una cadena de caracteres donde se tiene codigo que sera interpretado (y ejecutado) por esa funcion. En Flash era comun encontrar esta funcion (o algo parecido) en ActionScript 2 pero fue eliminada en la version 3.
Sin embargo haciendo uso del trabajo hecho en Tamarin Project de Mozilla, donde se tuvo acceso a parte del codigo del AVM2 osea el nuevo Action Virtual Machine que corre en el Flash Player 9.x y mas versiones mas recientes. Tamarin nos permite ver como funciona internamente el virtual machine, como se ejecuta el codigo, como se procesa, etc. Es algo bastante interesante y avanzado a la vez y permitiria la optimizacion de aplicaciones en la plataforma flash, incluso hay proyectos donde se promueve el uso de AOP o aplicaciones que convierten codigo de C# o Java a codigo ejecutable por el flash player... todo con la informacion que provee este proyecto; pero bueno, esto seran tema de otros posts.
Como decia anteriormente - antes de desviarme diciendo cuan impresionante es el proyecto - haciendo uso del proyecto Tamarin, "Metal Hurlant" (creador de multiples librerias open source como AS3Cripto) creo una libreria que se comporta como la funcion Eval, interpretando codigo de Actionscript en tiempo de ejecucion. Pueden acceder a un demo desde aqui.
Solo puedo decir que este es un trabajo brillante y que tomando las medidas de seguridad pertinentes (para que no se inyecte codigo maligno en tiempo de ejecucion entre otras) y no abusando de la misma por cuestiones de rendimiento (este proceso demanda algo de recursos) puede llegar a ser de mucha utilidad. La creacion de aplicaciones mas dinamicas, mas inteligentes en tiempo de ejecucion, etc. nos permitira ofrecer mejores aplicaciones a nuestros usuarios y a la vez creando aplicaciones menos complejas y flexibles... nuevamente sin abusar de esto...
Disfruten...
Etiquetas:
Actionscript3,
AS3,
Eval,
Flash Player,
Flex,
Mozilla,
Tamarin
jueves, 29 de octubre de 2009
Oferta de Empleo - Desarrolladores Flex en Costa Rica
Si hay algun interesado en conseguir empleo como Flex Developer en Costa Rica, la empresa Rivet Logic esta buscando 3 desarrolladores en Flex ojala con experiencia en el uso de frameworks como Mate y PureMVC y algo de BlazeDS
Mas informacion con Manuel Calvo, en mcalvo@rivetlogic.com y aqui.
* Yo solo paso el mensaje :), no soy responsable del mismo o parte de la empresa.
Mas informacion con Manuel Calvo, en mcalvo@rivetlogic.com y aqui.
* Yo solo paso el mensaje :), no soy responsable del mismo o parte de la empresa.
Etiquetas:
Costa Rica,
Flex,
Oferta Empleo,
Trabajo,
TrabajoCR
domingo, 9 de agosto de 2009
Flex Unit 4
Recientemente salió al aire Flex Unit 4 (versión Beta en este momento). Esta versión incorpora muchas mejoras, incluyendo mucha funcionalidad incorporada de la libreria para pruebas Fluint, quien le dono muchas mejoras a la misma, sobre todo en el manejo asincronomo.
Debemos recordar que en Flex/Flash estamos dentro de un ambiente asincronimo, esto es: hacemos el llamado a una función pero no sabemos cuando se hara devolvera el llamado a la función. Esto ya que el Flash Player no soporta multi hilos y al hacer uso de ciertos recursos que no tenemos en realidad una idea clara de cuanto va a durar en responder (llamado al servidor, cargar una pelicula, etc) puede hacer que la aplicación termine pegandose y la experiencia de usuario sea demasiado desagradable.
Una de las nuevas caracteristicas incorporadas en esta versión es el uso de metadatas. Esta es una caracteristica utilizada en Flex sobre todo cuando marcamos una propiedad como [Bindable]. Mucha gente no esta al tanto que en Actionscript 3 uno puede crear sus propias metadatas y utilizarlas como quieran dentro de su aplicación, lo cual a mi gusto es demasiado elegante.
Otra nueva caracteristica es que ya no se hace uso de la herencia (para crear nuevos tests y suites), ahora se usan metodos estaticos al estar utilizando metadatas. Ademas podemos ejecutar nuestros tests con distintos Runners (Flex Unit, Fluint, air, etc) que nos brindaran distintas caracteristicas de acuerdo a nuestras necesidades.
A continuacion unos pocos ejemplos de algunos principios. Espero poder agregar mas con el tiempo.
Creación de Suites:
Los suites son un conjunto de tests. Podemos tener dentro de un suite multiples tests.
Creando un Test con un delegate:
Creando Teorias:
Y que es esto? Es un nuevo concepto (al menos en el mundo de testing en Flex) Lo que nos dice es que si creemos que algo va a ejecutarse correctamente, tiene que ejecutarse correctamente con cualquier informacion que le sea pasada como parametro, por ejemplo, si debo encontrar que el valor absoluto de un numero va a ser siempre positivo, la teoria me dice que cualquier numero que le pase, va a ser mayor a 0. Entonces en mi test puedo poner un numero infinto de numeros a probar, solo que nosotros no tenemos tanto tiempo como para eso, entonces escogemos un conjunto finito de valores a probar y con ese conjunto validamos nuestra teoria.
Primero tengo que marcar la clase como que tiene una teoria, para que pueda ser procesada con el metadata [RunWith]
Ocupo 2 metadatas [ArrayElementType] utilizado en mxml para especificar el tipo de dato dentro de una coleccion y el metadata [DataPoints] que marca nuestros set como un conjunto para probar.
Marcamos el metodo conel metadata [Test] y el metadata [Theroy]
Esto hace que el test se ejecute la cantidad de datos que tengamos dentro de nuestro datapoint, en este caso se ejecutara automaicamente 3 veces.
Esto verdaderamente facilitara nuestro desarrollo en pruebas y nos permitira asegurarnos que podamos testear mejor nuestra aplicacion con menor esfuerzo
Otros Recursos
Introduccion (mejor link que he visto)
Excelente Tutorial
Pruebas Integracion
Importancia Pruebas y Feedback
Debemos recordar que en Flex/Flash estamos dentro de un ambiente asincronimo, esto es: hacemos el llamado a una función pero no sabemos cuando se hara devolvera el llamado a la función. Esto ya que el Flash Player no soporta multi hilos y al hacer uso de ciertos recursos que no tenemos en realidad una idea clara de cuanto va a durar en responder (llamado al servidor, cargar una pelicula, etc) puede hacer que la aplicación termine pegandose y la experiencia de usuario sea demasiado desagradable.
Una de las nuevas caracteristicas incorporadas en esta versión es el uso de metadatas. Esta es una caracteristica utilizada en Flex sobre todo cuando marcamos una propiedad como [Bindable]. Mucha gente no esta al tanto que en Actionscript 3 uno puede crear sus propias metadatas y utilizarlas como quieran dentro de su aplicación, lo cual a mi gusto es demasiado elegante.
Otra nueva caracteristica es que ya no se hace uso de la herencia (para crear nuevos tests y suites), ahora se usan metodos estaticos al estar utilizando metadatas. Ademas podemos ejecutar nuestros tests con distintos Runners (Flex Unit, Fluint, air, etc) que nos brindaran distintas caracteristicas de acuerdo a nuestras necesidades.
A continuacion unos pocos ejemplos de algunos principios. Espero poder agregar mas con el tiempo.
Creación de Suites:
Los suites son un conjunto de tests. Podemos tener dentro de un suite multiples tests.
package tests
{
[Suite]
[RunWith("org.flexunit.runners.Suite")]
public class DelegatesTestSuite
{
//Tests se declaran como propiedades
public var test1:MiClaseTest;
}
}
Creando un Test con un delegate:
package tests
{
public class MiClaseTest
{
//Metodo asincronomo:
//Marcamos el metodo como test y lo colocamos como primero
//en la lista. Ademas le decimos que espere al menos 3500 ms
//para que nos devuelvan algun valor
[Test (async, timeout="3500",order=1)]
public function loginUser():void
{
var user:User = new User()
user.username = "user2";
user.password = "123xyz";
var delegate:UserDelegate = new UserDelegate();
var responder:IResponder = Async.asyncResponder(this, new
TestResponder(onLoginUserComplete, onLoginUserFault), 3000);
}
//Este es el handler que recibira elresultado del
//servidor.
//@param event ResultEvent del servidor.
//@param token, es el objeto inicial que pasamos en el llamado
private function onLoginUserComplete(event:Object, token:Object):void
{
Assert.assertTrue(event.result.authenticated);
}
private function onInsertUserFail(event:FaultEvent, token:Object):void
{
Assert.fail('Error loading user: ' + event.fault);
}
}
}
Creando Teorias:
Y que es esto? Es un nuevo concepto (al menos en el mundo de testing en Flex) Lo que nos dice es que si creemos que algo va a ejecutarse correctamente, tiene que ejecutarse correctamente con cualquier informacion que le sea pasada como parametro, por ejemplo, si debo encontrar que el valor absoluto de un numero va a ser siempre positivo, la teoria me dice que cualquier numero que le pase, va a ser mayor a 0. Entonces en mi test puedo poner un numero infinto de numeros a probar, solo que nosotros no tenemos tanto tiempo como para eso, entonces escogemos un conjunto finito de valores a probar y con ese conjunto validamos nuestra teoria.
Primero tengo que marcar la clase como que tiene una teoria, para que pueda ser procesada con el metadata [RunWith]
Creando un sets de datos para probar:
package tests
{
[RunWith("org.flexunit.experimental.theories.Theories")]
public class MiClaseTest
{
...
Ocupo 2 metadatas [ArrayElementType] utilizado en mxml para especificar el tipo de dato dentro de una coleccion y el metadata [DataPoints] que marca nuestros set como un conjunto para probar.
Creacion de un test con teoria:
[DataPoints]
[ArrayElementType("Object")]
public static var usuariosValidos:Array = [
{username:"user1",password:"123xyz"},
{username:"user2",password:"344112"},
{username:"user3",password:"o5ii33"}
];
Marcamos el metodo conel metadata [Test] y el metadata [Theroy]
[Theory]
[Test(async,timeout="10000")]
public function validUsers(usuarios:Object):void
{
var delegate:UserDelegate = new UserDelegate();
var responder:IResponder = Async.asyncResponder(this, new
TestResponder(onUserLoginComplete, onUserLoginFail), 2500);
delegate.authUser(usuarios.username, usuarios.password);
}
Esto hace que el test se ejecute la cantidad de datos que tengamos dentro de nuestro datapoint, en este caso se ejecutara automaicamente 3 veces.
Esto verdaderamente facilitara nuestro desarrollo en pruebas y nos permitira asegurarnos que podamos testear mejor nuestra aplicacion con menor esfuerzo
Otros Recursos
Introduccion (mejor link que he visto)
Excelente Tutorial
Pruebas Integracion
Importancia Pruebas y Feedback
Suscribirse a:
Entradas (Atom)