» Publishers, Monetize your RSS feeds with FeedShow: More infos (Show/Hide Ads)
Dejo aquí todo el material de la charla que ofrecí en la PHPConfrence el viernes:
Slides

Ejemplo 1: Chat AMF

AMF File Chat: Chat realizado con WebOrb para PHP + Flex.
Se puede ver en acción la implementación de mensajería de WebOrb para PHP y un sistema de transferencia de archivos a través del canal del Chat.
Ejemplo 2: Gestor Contactos

Gestor de Contactos realizado con PHP + Flex. Se puede ver cómo con una misma base de código, solo cambiando las rutas de los gateways AMF se puede atacar a un mismo servicio a través de las diferentes implementaciones presentadas: Weborb, AMFPHP y Zend_AMF.
Nota 1: Zend framework debe estar instalado en el include_path de PHP para que funcione con Zend_AMF. Las demás implementaciones funcionan out of the box.
Nota 2: Se debe crear la base de datos MySQL gestor_contactos y ejecutar el script SQL contra ésta para crear la tabla contacto.
Enjoy!
Vuelve la PHP Barcelona Conference en su edición 2009 los días 30 y 31 de Octubre, este año con una apuesta mucho más fuerte e interesante. Para empezar el evento dura dos días durante los cuales habrá tres charlas/workshops paralelos continuamente.
Este año tengo el placer de participar como ponente dando la charla “Integrando PHP5 y ActionScript a través de AMF“, así que si vas a estar por ahí y te interesa ver cómo se integran PHP y Flash/Flex no puedes perderte esta charla!
La charla se centrará en el protocolo AMF, sus distintas implementaciones en PHP, casos de uso que cubran características interesantes, algunos benchmarks, etc…
Volviendo al evento, este año se volverá a celebrar en el Citilab de Cornellà de Llobregat, muy cerca de Barcelona ciudad.
El programa ya está cerrado y se puede ver en el apartado correspondiente de la web del evento.
Los ponentes: Derick Rethans, Pedro Cambra, Lorna Mitchel, Lorenzo Alberton, Joan Llenas (yo mismo), Rasmus Lerdorf, Kuassi Mensah, Chema Garrido, Erik Schultink, Lars Jankowfsky, Davide Ferrari, Stefan Koopmanschap, Lars Jankowfsky, Àlex Puig, Enrico Zimuel, Hans Zaunere, Damien Seguy, Davide Mendolia, Kuassi Mensah, Jordi Roura, Sebastian Bergmann, Kuassi Mensah, Victor Guardiola, Fabien Potencier, Fernando Palomo, Jordi Catà, David Zuelke… son un montón de ponentes!
Las charlas se darán en Inglés y Castellano dependiendo de la preferencia del ponente. En el apartado de charlas se puede diferenciar el idioma a través de un icono.
Si tienes que viajar y reservar estancia los organizadores proponen algunas opciones.
Y nada más solo queda que te registres!
Nos vemos
Hoy hace un año que disfruto de mi Macbook de 13″. Con su carcasa de aluminio, su teclado retro-iluminado, su trackpad multi-touch, su bonito diseño… una delicia en todos los sentidos :)
Todo empezó cuando entré a codesarrollar un proyecto con Xavi Beumala en communi.tv (antes de que Adobe lo abdujera). Debo decir que toda la culpa fue suya… él fue la primera persona que me demostró que el desarrollo de software con mac era posible. Yo por aquel entonces era el típico MACcéptico cargado de prejuicios que pensaba que un mac estaba limitado en lo que a herramientas de desarrollo se refiere, y no podía estar más equivocado… tras compartir proyecto durante un tiempo con él pude ver de primera mano la agilidad con la que trabajaba y la cantidad de herramientas de las que disponía. Eso fue el detonante de un proceso que varios meses después culminó con la adquisición de la mejor compra tecnológica que he hecho hasta el momento, mi macbook. El mismo día que salió la nueva generación de macbooks (después de la keynote), por la noche logré hacer la compra desde el Apple Store.
Debo decir que la transición de Windows a Mac ha sido mucho menos traumática de lo que en un principio pensé. Básicamente la organización del sistema de archivos, sobretodo los shorcuts y luego algunas peculiaridades del sistema operativo, pero en general nada que no se solucionara buscando un poco por internet…
Una de las cosas que me propuse cuando compré el macbook fue que quería deshacerme del ratón, y así lo hice. Desde entonces utilizo únicamente el trackpad, que es una maravilla, y el teclado. Esto me ha ayudado a centrarme mucho más en aprender los shortcuts de todas las aplicaciones y sacar partido de las funcionalidades multi-touch del trackpad. Esto además me permite trabajar igual de cómodamente en mi escritorio de la oficina que en el sofà de casa ya que no hecho en falta el ratón. El dejar el ratón quizás fue lo más complicado debo decir… pero ahora estoy totalmente adaptado y la verdad es que sufro menos (sinceramente, ni me acuerdo de la última vez) de dolores en el antebrazo y de contracturas en la espalda por la carga que supone el mantener las micro tensiones a las que obliga el utilizar ratón.
En conclusión, desde que tengo Mac trabajo más, mejor, soy más guapo, mejor persona y gano más dinero XDDD ;)
Un año más, una ciudad más ( aunque esta vez repetíamos porque que el lugar lo merecía ).
La ciudad invadida por un grupo de adictos a la tecnología, ocupando bares y terrazas pacificamente y cargando el aire estáticamente de conversaciones extrañas acerca de pixels y bytes… los de los bares no se quejan porque cuando vienen 50 personas a tu bar y te vacían las neveras todo está bien :)
Piscina, fútbol, levantamiento de jarra… todos deportes oficiales de este evento que año tras año acompañan las conversaciones, chistes y pases de fotos / vídeos más freaks “del intenné” ( de la mano de Marc Palau! ).
La culpa fue de marcos + colaboradores, los ponentes y por supuesto de todos y cada uno de los asistentes.
Este año en subflash se ha puesto claramente un acento en la parte más “designer” de las tecnologías Adobe con alguna excepción más “coder”. El balance ha sido muy positivo porque no hay nada más enriquecedor que salir de la cáscara y visualizar el colorido del mundo que tienes al lado.
La gente increíble como cada año. Unos nuevos y otros tantos polisubflashómanos (yo incluído).
Es curioso pero me he dado cuenta que el efecto subflash empieza mucho antes que el evento cuando te subes al coche. Cuando pones el primer CD y recoges al primer compañero de viaje, a partir de ahí ya está todo en marcha y realmente se nota porque tu mente ya deriva y las conversaciones tienden al infinito ( mis compañeros de viaje Ramón y Raúl ya me entienden :) ). Luego pones el segundo CD, recoges al segundo compañero, te encuentras con otro coche con el que has quedado y prosigues la ruta de Nciéntos Km hasta el lugar del evento. Cada momento es una emoción y esto es debido a que subflash es un evento muy especial. Este año se han hecho hipótesis de porqué a todos los asistentes nos parece tan especial y volvemos año tras año. Yo creo que son un montón de cosas mezcladas pero lo más importante es que marcos y los demás organizadores han conseguido mantener y potenciar este espíritu año tras año y es por eso que se merecen todo mis respeto y admiración.
El año que viene más!
Los bloques estáticos son bloques de código pertenecientes a una clase que se ejecutan una única vez cuando la clase en cuestión es cargada por primera vez en la AVM.
Dentro de un bloque estático se puede ejecutar cualquier tipo de expresión y referenciar a otras clases pero se debe tener en cuenta que desde el bloque, al estar dentro del ámbito de la clase (no de la instancia, de ahí que se llamen bloques estáticos), solo se pueden referenciar las propiedades y métodos estáticos de la clase.
Un bloque estático es equiparable a un constructor pero dentro del ámbito de clase (no de instancia).
El siguiente código muestra el uso de un bloque estático a modo de inyección de dependencias dinámico:
-
/* Archivo.as */
-
package com.joangarnet.persistencia {
-
-
import flash.system.Capabilities;
-
-
public class Archivo implements IArchivo{
-
static private var archivoImpl:IArchivo;
-
-
/* bloque estático */
-
{
-
if( Capabilities.playerType == "Desktop" ){
-
// guarda en el sistema de archivos del O.S.
-
archivoImpl = new ArchivoAIR();
-
}else{
-
// guarda en web
-
archivoImpl = new ArchivoFlex();
-
}
-
trace( Capabilities.playerType );
-
}
-
-
/*
-
* implementacion de IArchivo
-
*/
-
public function guardar():void{
-
archivoImpl.guardar();
-
}
-
-
public function borrar():void{
-
archivoImpl.borrar();
-
}
-
}
-
}
-
-
/* IArchivo.as */
-
package com.joangarnet.persistencia{
-
public interface IArchivo{
-
function guardar():void;
-
function borrar():void;
-
}
-
}
-
-
/* ArchivoAIR.as */
-
package com.joangarnet.persistencia{
-
public class ArchivoAIR implements IArchivo{
-
public function guardar():void{
-
trace("guardar() en el sistema de archivos local")
-
}
-
-
public function borrar():void{
-
trace("borrar() en el sistema de archivos local")
-
}
-
}
-
}
-
-
/* ArchivoFlex.as */
-
package com.joangarnet.persistencia{
-
public class ArchivoFlex implements IArchivo{
-
public function guardar():void{
-
trace("guardar() vía web")
-
}
-
-
public function borrar():void{
-
trace("borrar() vía web")
-
}
-
}
-
}
TestBloqueEstatico.mxml
-
<?xml version = "1.0" encoding = "utf-8"?>
-
<mx:Application xmlns:mx = "http://www.adobe.com/2006/mxml" layout = "absolute">
-
<mx:Script>
-
<![CDATA[
-
import com.joangarnet.persistencia.Archivo;
-
-
private var f:Archivo = new Archivo();
-
]]>
-
</mx:Script>
-
<mx:Button click = "f.borrar()" label="borrar" />
-
<mx:Button click = "f.guardar()" label="guardar" />
-
</mx:Application>
Si se ejecuta en el contexto de una aplicación AIR y clicamos en los dos botones borrar y guardar respectivamente se mostrará en la consola:
borrar() en el sistema de archivos local guardar() en el sistema de archivos local
Si de lo contrario se hace desde el navegador se mostrará:
borrar() vía web guardar() vía web

Voy a dar una charla durante el evento AdobeEnVivo que se dará online durante la semana que viene, del martes 14 al sábado 18.
La charla, que será el jueves 16 a las 16h (hora en España: 22h), intentará dar una visión clara de las cosas que se pueden hacer con AIR que no se pueden hacer con el Flash Player. Será un recorrido por las distintas APIs que caracterizan el runtime de AIR.
Finalmente puede que haya algo de reflexión de lo qué es AIR, el sentido que tiene y el lugar que ocupa en el mercado, etc....
Aparte de mi aportación, la parrilla del evento es MUY extensa y los ponentes hablarán de cosas muy variopintas, desde After Effects hasta Flex 4 pasando por Flash Lite, Catalyst, Cold Fusion, Live Cycle, etc...
El evento contará con la participación de un montón de Adobe User Group managers, co-managers, y otras personalidades de la escena Adobiana.
Si trabajas con productos de Adobe y hablas castellano seguro que encuentras una o más charlas de tu interés.
Ayer me enteré de que nuestro querido exiliado Xavi Beumala tiene nuevo blog: http://www.rialvalue.com/blog/.
Con lo ocupado que anda no sé qué nivel de actualización tendrá pero estoy seguro que el contenido será excelente y de gran interés para todos los Flasheros / Flexeros.

La comunidad de desarrollo Subflash ha pegado un estirón y ha dejado de ser exclusivamente un foro, de hecho esto es una cosa que la organización lleva años preparando y que por fin ha dado su fruto.
Desde hace unos días cuando accedes a subflash.com te encuentras un blog repleto de información referente a la organización de los talleres de verano.

Y hablando de los talleres de verano... no está de más mencionar que ya se han abierto las inscripciones para este año que, al igual que el pasado, van a tener lugar en Alicante. Será durante los días 28, 29 y 30 de Agosto en la Villa Universitaria de Alicante a un precio inmejorable de 100 EUR.
Ahora mismo quedan 32 plazas ( yo ya he reservado la mía! ) de las 50 de las que se dispone, lo cuál no está nada mal teniendo en cuenta que solo hay una charla confirmada :)
UPDATE: ya son 2 charlas confirmadas!
Dejo aquí algunos enlaces de interés:
- Micro site talleres 2009
- Parrilla de sesiones
- Formulario de registro
- Acceso a los micro sites de otros años
Nos vemos en Alicante!
Bueno, ha sido un día intenso...
El evento ha sido todo un éxito y me siento muy satisfecho en todos los aspectos. He aprendido un montón, he visto a viejos amigos, he conocido a nuevos, he visto de primera mano mis nuevos juguetitos en acción... me he pasado todo el dia spameando por Twitter las charlas...
Pido disculpa a los que me siguen en Twitter y no les interesa Flex... habrán flipado! por otra parte agradezco a los que habéis estado comentando la jugada y me habéis animando ( aunque no os he podido responder por falta de aliento lo he apreciado, de verdad! a ver si mañana o pasado repaso las preguntas y las respondo en un post separado ). La verdad es que al final ha llegado un momento en el que sentía una responsabilidad muy grande por retransmitir el evento, que tontería... Tú dame el canto de una moneda y te monto un escenario XD
En madeinflex he posteado un resumen del evento.
Si queréis seguir el evento en directo podéis hacerlo desde mi cuenta de Twitter!
Solo una recordatorio para aquellos que todavía no se hayan enterado…
Ya estan disponibles las beta 1 de Flash Builder 4 y Flash Catalyst!
Más info:
En esta segunda parte ( ver parte 1 ) de la serie de posts relacionados con la implementación del patrón de diseño Memento voy a mostrar una versión mucho más genérica que simplifica su reutilización en cualquier contexto.
Los objetivos que me he marcado para realizar esta implementación son básicamente dos:
- Reutilización: Crear una librería que implemente Memento de tal forma que se pueda utilizar sin tener que preocuparse de crear clases Memento concretas.
- Persistencia: Crear una capa de persistencia para que los Mementos se puedan almacenar no solo en memoria sino tambien en un SharedObject, en una base de datos, en un archivo, etc..
Estructura del proyecto de ejemplo en Flex builder:

Si comparamos la estructura del proyecto con la de la versión del primer post se pueden ver una serie de diferencias que voy a enumerar a continuación.
- ObjectSnapshot: Para empezar la interfaz
IMementoya no está y se ha sustituído por la claseObjectSnapshot. Esta clase es la implementación maestra de un Memento. Gracias a esto ya nunca más tendremos que preocuparnos de implementar un Memento. Extendiendo de la claseflash.utils.Proxyse ha podido crear una implementación que cabe en cualquier escenario debido a la potencia que nos ofrece el hecho que es una clase dinámica (dynamic). Como los Mementos no son objetos que se requieran en escenarios de gran rendimiento ( yo no voy a más de 2 CTRL+Z por segundo.. :) ) el hecho de utilizar una clase dinámica no nos penaliza. - IRestorable: Esta interfaz sustituye a
IOriginatorde la otra versión. Es básicamente lo mismo pero con otro nombre más significativo (para mi) y añadiendo par de métodos más que obligan a los implementores a disponer de unIPersistable. - IPersistable: Interfaz que deben implementar todos los drivers que van a permitir guardar los Mementos (ObjectSnapshots) en distintos soportes. La condición que impone a los implementores es disponer de un método write() y otro read().
- carpeta persisters: Una colección de implementaciones base de
IPersistable. Se puede ver que una limitación que tienen estos objetos es que las peticiones deben siempre síncronas. Igual en un post futuro esto cambia...
Los conceptos son los mismos pero he intantado adaptar la implementación para que saque más partido de las características de ActionScript 3.
Lo mejor es descargar el ejemplo y jugar un poco:
Descarga ejemplo
En el zip hay un proyecto ActionScript para Flex Builder.
Acabo de actualizar la instalación de wordpress a la última versión, si alguien ve cosas raras agradeceré que me avise.
Por lo demás todo bien... gracias :)

El otro día se me planteó la duda de si sería posible hacer algo parecido a un gestor de descargas con AIR. Después de experimentar un poco con las APIs de network junto con un poco de investigación de cómo trabaja el protocolo HTTP internamente he sacado un prototipo de lo que podría ser el core de este hipotético gestor de descargas.
Aquí enumero las funcionalidades que se esperan de un gestor de descargas y si están implementadas en el prototipo:
- pause / resume download:
Esta es la funcionalidad más importante y esencial de un gestor de descargas. La posibilidad de pausar / reanudar el proceso de descargan tantas veces como sea necesario.
Esta funcionalidad si está implementada en el prototipo. - multipart download:
Esta funcionalidad no es esencial pero si un plus a tener en cuenta para cualquier gestor de descargas que se precie. La posibilidad de segmentar la descarga de un mismo archivo en varios hilos de tal forma que el proceso de descarga sea más rápido, hasta un 200% más rápido (existen limitaciones en la velocidad de descarga de las conexiones individuales).
Esta funcionalidad si está implementada en el prototipo. - Posibilidad de persistir la sesión de descarga:
Esta funcionalidad es esencial sobretodo si se quiere descargar grandes archivos que requieren que se reanude la descarga después de cerrar la aplicación o incluso hacer un reboot del sistema operativo. También es útil en el caso de caídas de red o cuelgues de la aplicación (crash recovery).
Esta funcionalidad no está implementada en el prototipo.
Descarga ejemplo
Descarga el código fuente del prototipo
He hecho un upgrade del software de este blog (wordpress). Si alguien ve algo raro es que probablemente algo ha ido mal y se tiene que revisar.
Agradecería un simple aviso a través de un comentario aquí mismo si se da el caso.
Gracias!
Introducción
Una de las funcionalidades más populares de cualquier programa que maneje datos es la de hacer/deshacer. Dicha funcionalidad se basa en la captura del estado de los distintos objetos que participan en una determinada tarea y su posterior recuperación.
La implementación de un sistema horizontal para la gestión del estado de los objetos en una aplicación es un tema complejo. El libro de Gof "Design Patterns: Elements of Reusable Object-Oriented Software" plantea una solución al problema bautizada como Memento.
Problema
Lo que queremos conseguir es capturar el estado de un objeto en un momento dado y externaizarlo de tal forma que más tarde pueda ser recuperado y devuelto al objeto. Todo esto sin romper el encapsulamiento.
Actores y colaboraciones
En el patrón de diseño Memento, según descrito en el libro de Gof, intervienen tres actores:
- Originator:
Es el objeto del que queremos guardar el estado. Puede ser un formulario entero, un campo de texto, un player de video...
- Memento:
Es un contenedor dónde guardar el estado del objeto Originator. Un estado, un Memento. Es una clase que solo contiene las propiedades correspondientes al estado del Objeto Originator y no requiere de lógica alguna.
- Caretaker:
Es el lugar dónde se almacenan los Mementos. Es la clase que tiene que mantener ordenadamente a salvo los distintos Mementos de un Originator determinado para poder devolvérselos cuando éste los requiera.
Implementación
Para esta implementación de ejemplo he utilizado la clase fl.controls.TextArea del set de componentes visuales de Flash y la he extendido para que tenga la capacidad de crear y recuperar Mementos.
La estructura del proyecto es la que se puede ver en la siguiente imágen:

En el package com.joangarnet.patterns.memento podemos ver una clase y dos interfaces que presumiblemente son el sistema transversal de hacer/deshacer en nuestra aplicación ficticia. Vamos a ver qué hacen.
IMemento.as
Ésta es la más las más fácil. IMemento es una interfaz vacía que nos aporta un tipo común para todas las clases Memento. Es decir, cualquier Memento en nuestra aplicación implementará esta interfaz para poder actuar cómo tal.
-
package com.joangarnet.patterns.memento {
-
public interface IMemento {
-
}
-
}
IOriginator.as
Esta interfaz, a implementar por todos los Originators, obliga a los implementores a definir dos métodos muy importantes. Uno es createMemento y da la funcionalidad de crear y devolver un Memento a partir del estado actual del objeto, el otro es setMemento y da la funcionalidad de, a partir de un Memento dado, reestablecer el estado del objeto.
-
package com.joangarnet.patterns.memento {
-
public interface IOriginator {
-
function setMemento( memento:IMemento ):void;
-
function createMemento():IMemento;
-
}
-
}
MementoCaretaker.as
Esta clase hace de contenedor genérico de Mementos. Dispone de tres métodos a tener en cuenta. Uno es addMemento y sirve para guardar un memento de un objeto determinado, otro es undo y sirve para reestablecer el estado de un objeto dado al último Memento guardado, finalmente tenemos redo que hace los mismo pero deshaciendo lo que hemos deshecho... La clase es un Singleton porque me ha parecido lo suficientemente justificado que lo sea ya que al fin y al cabo es desde esta clase que se gestionan todos los mementos y no tiene sentido que exista otra instancia.
-
package com.joangarnet.patterns.memento {
-
import flash.utils.Dictionary;
-
-
public class MementoCaretaker {
-
private var undoMementos:Dictionary;
-
private var redoMementos:Dictionary;
-
-
public function MementoCaretaker(se:SE) {
-
undoMementos = new Dictionary(true);
-
redoMementos = new Dictionary(true);
-
}
-
-
public function addMemento( host:IOriginator, memento:IMemento ):void {
-
redoMementos = new Dictionary(true);
-
-
if (! undoMementos[host]) {
-
undoMementos[host] = [];
-
}
-
(undoMementos[host] as Array).push(memento);
-
}
-
-
public function undo( host:IOriginator ):IMemento {
-
if (canUndo(host)) {
-
if (! redoMementos[host]) {
-
redoMementos[host] = [];
-
}
-
var lastMemento:IMemento = (undoMementos[host] as Array).pop();
-
(redoMementos[host] as Array).push( lastMemento );
-
return lastMemento;
-
}
-
return null;
-
}
-
-
public function redo( host:IOriginator ):IMemento {
-
if (canRedo(host)) {
-
var nextMemento:IMemento = (redoMementos[host] as Array).pop();
-
(undoMementos[host] as Array).push( nextMemento );
-
return nextMemento;
-
}
-
return null;
-
}
-
-
public function canUndo( host:IOriginator ):Boolean {
-
return undoMementos[host] && (undoMementos[host] as Array).length> 0;
-
}
-
-
public function canRedo( host:IOriginator ):Boolean {
-
return redoMementos[host] && (redoMementos[host] as Array).length> 0;
-
}
-
-
public function getRedos( host:IOriginator ):uint {
-
if( !redoMementos[host] ) {
-
return 0;
-
}
-
return (redoMementos[host] as Array).length;
-
}
-
-
public function getUndos( host:IOriginator ):uint {
-
if( !undoMementos[host] ) {
-
return 0;
-
}
-
return (undoMementos[host] as Array).length;
-
}
-
-
// Singleton implementation
-
private static var instance:MementoCaretaker;
-
public static function getInstance():MementoCaretaker {
-
return instance || (instance = new MementoCaretaker(new SE()));
-
}
-
}
-
}
-
class SE{/* Singleton Enforcer */}
En el package com.joangarnet.flash.controls tenemos la parte más específica en la que sacamos partido a la implementación genérica de memento que ya tenemos hecha.
Para este ejemplo he escogido el TextArea porque se parece a un editor de textos, que es a lo que más acostumbrados estamos todos a ver con funcionalidad de hacer/deshacer. ¿Qué sería un editor de textos sin hacer/deshacer? de hecho nuestro textarea será mejor que el viejo Notepad de Windows, que solo tiene un único deshacer! Es eso posible todavía? pues se ve que si...
UndoableTextArea.as
Éste es nuestro primer Originator. Otros candidatos serían UndoableTextInput, UndoableColorPicker, UndoableSlider, etc... Cómo buen originator debe implementar y satisfacer la interfaz IOriginator.
-
package com.joangarnet.flash.controls {
-
import com.joangarnet.patterns.memento.IMemento;
-
import com.joangarnet.patterns.memento.IOriginator;
-
-
import fl.controls.TextArea;
-
-
public class UndoableTextArea extends TextArea implements IOriginator {
-
public function UndoableTextArea() {
-
super();
-
}
-
-
public function setMemento( memento:IMemento ):void {
-
if (memento) {
-
var m:UndoableTextAreaMemento = memento as UndoableTextAreaMemento;
-
this.text = m.text;
-
this.verticalScrollPosition = m.verticalScrollPosition;
-
this.horizontalScrollPosition = m.horizontalScrollPosition;
-
}
-
}
-
-
public function createMemento():IMemento {
-
return new UndoableTextAreaMemento( this.text, this.verticalScrollPosition, this.horizontalScrollPosition );
-
}
-
}
-
}
UndoableTextAreaMemento.as
Si tenemos un Originator para un TextArea entonces es obligado disponer de su Memento. Éste es el papel de esta clase. Cosas a notar de la implementación de una clase Memento. Primero debe implementar la interfaz IMemento, segundo todas las propiedades de estado que se le asignan se pasan como parámetros al constructor y son luego accesibles a través de getters. Todo esto es para mantener el encapsulamiento. Como norma general es preferible que los mementos guarden propiedades de tipos primitivos. Esto ayudará por ejemplo a evitar que por error se pueda llegar a modificar el estado del memento a través de una referencia, o también nos permitirá más tarde añadir una capa de persistencia fácilmente (lo veremos en otro post).
-
package com.joangarnet.flash.controls
-
{
-
import com.joangarnet.patterns.memento.IMemento;
-
-
public class UndoableTextAreaMemento implements IMemento {
-
private var _horizontalScrollPosition:Number;
-
private var _verticalScrollPosition:Number;
-
private var _text:String;
-
-
public function UndoableTextAreaMemento( text:String, verticalScrollPosition:Number, horizontalScrollPosition:Number ) {
-
this._verticalScrollPosition = verticalScrollPosition;
-
this._horizontalScrollPosition = horizontalScrollPosition;
-
this._text = text;
-
}
-
-
public function get horizontalScrollPosition():Number {
-
return _horizontalScrollPosition;
-
}
-
-
public function get verticalScrollPosition():Number {
-
return _verticalScrollPosition;
-
}
-
-
public function get text():String {
-
return _text;
-
}
-
}
-
}
MementoSample_1.as
Finalmente el punto de entrada a la aplicación. La clase que Flash tiene asignada como DocumentClass ( creo que se llama así... ) y que monta el ejemplo.
Aquí la gestión de los mementos es externa a su Originator pero no habría ningún problema en que cada Originator escuchara a los eventos de teclado y se autogestionara los mementos...
-
package {
-
import com.joangarnet.flash.controls.UndoableTextArea;
-
import com.joangarnet.patterns.memento.MementoCaretaker;
-
-
import fl.controls.Button;
-
-
import flash.display.Sprite;
-
import flash.events.Event;
-
import flash.events.MouseEvent;
-
-
public class MementoSample_1 extends Sprite {
-
private var tf:UndoableTextArea;
-
private var undo:Button;
-
private var redo:Button;
-
-
public function MementoSample_1() {
-
undo = new Button();
-
undo.label = "undo (0)";
-
undo.x = 30; undo.y = 25;
-
undo.addEventListener( MouseEvent.CLICK, undoHandler );
-
-
redo = new Button();
-
redo.label = "redo (0)";
-
redo.x = undo.x + undo.width + 5; redo.y = 25;
-
redo.addEventListener( MouseEvent.CLICK, redoHandler );
-
-
tf = new UndoableTextArea();
-
tf.x = 30; tf.y = 50;
-
tf.width = 200; tf.height = 100;
-
tf.addEventListener( Event.CHANGE, changeHandler );
-
-
addChild( undo );
-
addChild( redo );
-
addChild( tf );
-
}
-
-
private function changeHandler( event:Event ):void {
-
MementoCaretaker.getInstance().addMemento( tf, tf.createMemento() );
-
printAvailableMementos();
-
}
-
-
private function undoHandler( event:MouseEvent ):void {
-
tf.setMemento( MementoCaretaker.getInstance().undo(tf) );
-
printAvailableMementos();
-
}
-
-
private function redoHandler( event:MouseEvent ):void {
-
tf.setMemento( MementoCaretaker.getInstance().redo(tf) );
-
printAvailableMementos();
-
}
-
-
private function printAvailableMementos():void {
-
undo.label = "undo (" + MementoCaretaker.getInstance().getUndos(tf).toString() + ")";
-
redo.label = "redo (" + MementoCaretaker.getInstance().getRedos(tf).toString() + ")";
-
}
-
}
-
-
}
Conclusión
Como se puede ver una vez se tiene la base es muy sencillo implementar esta funcionalidad. Hacerlo para otros componentes del set de componentes de flash es una cuestión tan trivial como saber determinar qué propiedades queremos guardar en un memento, crear el Memento y la subclase del componente escogido para que implemente IOriginator.
En una entrada próxima creo que será interesante evolucionar un poco más la implementación de este patrón de diseño para añadir por ejemplo una capa de persistencia y quizás abstraer incluso más la gestión de los Mementos para evitar tener que crear un Memento para cada clase Originator.
UPDATE: ver parte 2: "Memento: estado de un objeto (II)"
Descarga ejemplo
En el zip hay proyectos para Flash CS4 y para Flex 3.
No es broma, aunque algunos conocen ya mi doble personalidad todavía tengo pequeños malentendidos con este tema (como es normal) y creo que no está de más contar qué pasa con mi nombre...
Por si alguien no se ha pasado todavía por la página de about y ha leído las cuatro líneas que escribí en su día mi nombre real es Joan Llenas Masó.
Joan Garnet es un nickname, seudónimo, alias, nombre de guerra, etc... que se compone de mi nombre de pila 'Joan' y la palabra 'garnet' que en inglés significa, entre otras cosas, granate.
Así pues se podría decir que soy Joan Granate, que si hurgamos un poco más viene de 'Joan Vermell' (Joan Rojo en catalán) o de Ketchup, ambos apodos que me pusieron cuando era un pre-adolescente y que han ido metamorfoseando a medida que he ido creciendo. En estatura, claro :)
El SMAUG BlocketPC ha organizado un evento especial para celebrar su tercer aniversario como AUG. El evento va girar entorno a la Plataforma Flash y su integración con dispositivos y móbiles, un tema del que el equipo de BlocketPC siempre nos ha tenido al día y que sin duda ahora, más que nunca, va a dar mucho de que hablar.
Si quieres saber las últimas novedades acerca de este tema de la mano del mismisimo Mark Doherty (Platform Evangelist at Adobe on Mobile and Devices), de Zed, de Thumbplay o del propio equipo de BlocketPC solo tienes que registrate. Es totalmente gratuito e indoloro, lo peor que te puede pasar es que te vuelvas a casa con un montón de información útil acerca de cómo vas a integrar tu nueva killer app en la nevera de casa o con uno de los premios que se sortearán al finalizar el evento entre los asistentes: un Adobe Web Premium CS4, un Nokia E71 o merchandising.
Revisando la implementación de la clase mx.collections.XMLListCollection del sdk de Flex he dado con otra clase mx.utils.XMLNotifier que ha desvelado un secreto que la verdad todavía no entiendo como no me había picado la curiosidad antes...
La clase XML no es un EventDispatcher, hereda directamente de Object, por lo tanto no disponen del mecanismo de notificación de eventos. Sin embargo la clase XMLListCollection dispone de esta funcionalidad. ¿Cómo lo hace?
La respuesta está en un método indocumentado de la clase XML:
function setNotification( callback:Function ):void;
La implementación de callback sería:
function xmlNotification(currentTarget:Object, type:String, target:Object, value:Object, detail:Object):void;
Este método registra una función a modo de callback para que cada nodo XML pueda notificar sus cambios a través del mismo. Al más puro estilo AS1... supongo que por eso está indocumentado. Algún día veremos una clase XML heredando de EventDispatcher y exponiendo toda esta funcionalidad con orgullo y documentación? quién sabe...
De momento esto podría ser un principio:
-
/**
-
* Método que registra una función de callback para que una instancia de XML pueda notificar
-
* cambios.
-
* Los tipo de modificaciones que son notificadas a través del callback son:
-
* - attributeAdded: Al añadir un atributo.
-
* - attributeChanged: Al modificar un atributo.
-
* - attributeRemoved: Al eliminar un atributo.
-
* - nodeAdded: Al añadir un nodo.
-
* - nodeChanged: Al modificar un nodo.
-
* - nodeRemoved: Al eliminar un nodo.
-
* - nameSet: Al asignar el nombre de un nodo.
-
* - namespaceAdded: Al añadir un espacio de nombres al nodo.
-
* - namespaceRemoved: Al eliminar un espacio de nombres al nodo.
-
* - namespaceSet: Al añadir un espacio de nombres al nodo.
-
* - textSet: Al asignar un nodo text en el nodo.
-
* @param callback: Referencia a la función callback.
-
*/
-
function setNotification( callback:Function ):void;
Y un ejemplo:
-
<?xml version="1.0" encoding="utf-8"?>
-
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="f()">
-
<mx:Script>
-
<![CDATA[
-
import mx.utils.ObjectUtil;
-
private function f(){
-
var xml:XML =
-
<root>
-
<node id="1"/>
-
</root>;
-
xml.setNotification( callback );
-
-
xml.children()[0].@newAttr = "hola";//attributeAdded
-
xml.children()[0].@id = "a";//attributeChanged
-
delete xml.children()[0].@id;//attributeRemoved
-
xml.appendChild( <node1 id="2">Lorem Ipsum</node1> );//nodeAdded
-
delete xml.children()[0];//nodeRemoved
-
(xml.children()[0] as XML).setName("node1Renamed");//nameSet
-
var ns1:Namespace = new Namespace("w1", "http://www.test.com/w1/");
-
var ns2:Namespace = new Namespace("w2", "http://www.test.com/w2/");
-
xml.addNamespace( ns1 );//namespaceAdded
-
xml.addNamespace( ns2 );//namespaceAdded
-
xml.setNamespace( ns1 );//namespaceSet
-
xml.setNamespace( ns2 );//namespaceSet
-
xml.removeNamespace( ns1 );//namespaceRemoved
-
xml.children()[0] = "hola2";//textSet + nodeChanged
-
-
function callback(targetCurrent:Object, command:String, target:Object, value:Object, detail:Object):void{
-
trace( command )
-
}
-
-
}
-
]]>
-
</mx:Script>
-
</mx:Application>








