FlashDay: evento inspiracional

El próximo día 4 de Marzo tendrá lugar la primera edición del FlashDay, evento concebido y organizado por Xavi Beumala y Enrique Duvós como iniciativa personal fuera del ámbito de Adobe.
El evento tiene como objetivo ofrecer un entorno de encuentro en el que se pueda mostrar lo que la plataforma Flash permite hacer a nivel experimental o conceptual.
No se pretende mostrar código sino qué hace el código, además con la premisa de que lo que se muestre sea innovador e inspirador. Esto hace que sea un evento válido, no solo para programadores sino para cualquiera persona que esté interesada en la tecnología Flash como herramienta creativa.



Por mi parte he sido invitado para formar parte del grupo fijo de ponentes.
Si os fijáis en la Agenda del evento veréis que hay un tal Joan Llenas, ese soy yo. En esta ocasión dejaré el disfraz de Joan Garnet en el armario… :)
En mis 30 minutos de gloria voy a mostrar algunos ejemplos de lo que se puede hacer con las nuevas funcionalidades de generación dinámica de audio (a.k.a síntesis de audio) del Flash Player, un campo apasionante dentro del mundo de la programación del que desde hace bien poquito podemos sacar partido gracias a las nuevas APIs introducidas en el Flash Player 10.

Si estas por Barcelona el día 4 de Marzo no dejes de reservarte 5 horitas (de de 14 a 19h) para asistir al evento.
Y recuerda esto: No sirve de nada que te lo expliquen, tienes que venir!

Material: Integrando PHP5 y AS3 a través de AMF

Dejo aquí todo el material de la charla que ofrecí en la PHPConfrence el viernes:

Slides

Integrando ActionScript3 y PHP5 a través de AMF

Ejemplo 1: Chat AMF

Flex + WebOrb Chat
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 Contactos multi AMF implementation
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!

PHP Conference 2009

php barcelona conference 2009Vuelve 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…flex and PHP

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

Bloques estáticos en AS3

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:

Actionscript:
  1. /* Archivo.as */
  2. package com.joangarnet.persistencia {
  3.    
  4.     import flash.system.Capabilities;
  5.    
  6.     public class Archivo implements IArchivo{
  7.         static private var archivoImpl:IArchivo;
  8.        
  9.         /* bloque estático */
  10.         {
  11.             if( Capabilities.playerType == "Desktop" ){
  12.                 // guarda en el sistema de archivos del O.S.
  13.                 archivoImpl = new ArchivoAIR();
  14.             }else{
  15.                 // guarda en web
  16.                 archivoImpl = new ArchivoFlex();
  17.             }
  18.             trace( Capabilities.playerType );
  19.         }
  20.        
  21.         /* 
  22.          * implementacion de IArchivo
  23.          */
  24.         public function guardar():void{
  25.             archivoImpl.guardar();
  26.         }
  27.        
  28.         public function borrar():void{
  29.             archivoImpl.borrar();
  30.         }
  31.     }
  32. }
  33.  
  34. /* IArchivo.as */
  35. package com.joangarnet.persistencia{
  36.     public interface IArchivo{
  37.         function guardar():void;
  38.         function borrar():void;
  39.     }
  40. }
  41.  
  42. /* ArchivoAIR.as */
  43. package com.joangarnet.persistencia{
  44.     public class ArchivoAIR implements IArchivo{
  45.         public function guardar():void{
  46.             trace("guardar() en el sistema de archivos local")
  47.         }
  48.        
  49.         public function borrar():void{
  50.             trace("borrar() en el sistema de archivos local")
  51.         }
  52.     }
  53. }
  54.  
  55. /* ArchivoFlex.as */
  56. package com.joangarnet.persistencia{
  57.     public class ArchivoFlex implements IArchivo{
  58.         public function guardar():void{
  59.             trace("guardar() vía web")
  60.         }
  61.        
  62.         public function borrar():void{
  63.             trace("borrar() vía web")
  64.         }
  65.     }
  66. }

TestBloqueEstatico.mxml

XML:
  1. <?xml version = "1.0" encoding = "utf-8"?>
  2. <mx:Application xmlns:mx = "http://www.adobe.com/2006/mxml" layout = "absolute">
  3.     <mx:Script>
  4.         <![CDATA[
  5.             import com.joangarnet.persistencia.Archivo;
  6.  
  7.             private var f:Archivo = new Archivo();
  8.         ]]>
  9.     </mx:Script>
  10.     <mx:Button click = "f.borrar()" label="borrar" />
  11.     <mx:Button click = "f.guardar()" label="guardar" />
  12. </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

Memento: estado de un objeto (II)

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:

Estructura del proyecto de ejemplo en Flex builder:
memento project

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.

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.

Memento: estado de un objeto (I)

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:

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.

Actionscript:
  1. package com.joangarnet.patterns.memento {
  2.     public interface IMemento {
  3.     }
  4. }

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.

Actionscript:
  1. package com.joangarnet.patterns.memento {
  2.     public interface IOriginator {
  3.         function setMemento( memento:IMemento ):void;
  4.         function createMemento():IMemento;
  5.     }
  6. }

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.

Actionscript:
  1. package com.joangarnet.patterns.memento {
  2.     import flash.utils.Dictionary;
  3.  
  4.     public class MementoCaretaker {
  5.         private var undoMementos:Dictionary;
  6.         private var redoMementos:Dictionary;
  7.  
  8.         public function MementoCaretaker(se:SE) {
  9.             undoMementos = new Dictionary(true);
  10.             redoMementos = new Dictionary(true);
  11.         }
  12.  
  13.         public function addMemento( host:IOriginator, memento:IMemento ):void {
  14.             redoMementos = new Dictionary(true);
  15.            
  16.             if (! undoMementos[host]) {
  17.                 undoMementos[host] = [];
  18.             }
  19.             (undoMementos[host] as Array).push(memento);
  20.         }
  21.  
  22.         public function undo( host:IOriginator ):IMemento {
  23.             if (canUndo(host)) {
  24.                 if (! redoMementos[host]) {
  25.                     redoMementos[host] = [];
  26.                 }
  27.                 var lastMemento:IMemento = (undoMementos[host] as Array).pop();
  28.                 (redoMementos[host] as Array).push( lastMemento );
  29.                 return lastMemento;
  30.             }
  31.             return null;
  32.         }
  33.        
  34.         public function redo( host:IOriginator ):IMemento {
  35.             if (canRedo(host)) {
  36.                 var nextMemento:IMemento = (redoMementos[host] as Array).pop();
  37.                 (undoMementos[host] as Array).push( nextMemento );
  38.                 return nextMemento;
  39.             }
  40.             return null;
  41.         }
  42.  
  43.         public function canUndo( host:IOriginator ):Boolean {
  44.             return undoMementos[host] && (undoMementos[host] as Array).length> 0;
  45.         }
  46.        
  47.         public function canRedo( host:IOriginator ):Boolean {
  48.             return redoMementos[host] && (redoMementos[host] as Array).length> 0;
  49.         }
  50.        
  51.         public function getRedos( host:IOriginator ):uint {
  52.             if( !redoMementos[host] ) {
  53.                 return 0;
  54.             }
  55.             return (redoMementos[host] as Array).length;
  56.         }
  57.        
  58.         public function getUndos( host:IOriginator ):uint {
  59.             if( !undoMementos[host] ) {
  60.                 return 0;
  61.             }
  62.             return (undoMementos[host] as Array).length;
  63.         }
  64.  
  65.         // Singleton implementation
  66.         private static var instance:MementoCaretaker;
  67.         public static function getInstance():MementoCaretaker {
  68.             return instance || (instance = new MementoCaretaker(new SE()));
  69.         }
  70.     }
  71. }
  72. 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.

Actionscript:
  1. package com.joangarnet.flash.controls {
  2.     import com.joangarnet.patterns.memento.IMemento;
  3.     import com.joangarnet.patterns.memento.IOriginator;
  4.    
  5.     import fl.controls.TextArea;
  6.    
  7.     public class UndoableTextArea extends TextArea implements IOriginator {
  8.         public function UndoableTextArea() {
  9.             super();
  10.         }
  11.        
  12.         public function setMemento( memento:IMemento ):void {
  13.             if (memento) {
  14.                 var m:UndoableTextAreaMemento = memento as UndoableTextAreaMemento;
  15.                 this.text = m.text;
  16.                 this.verticalScrollPosition =  m.verticalScrollPosition;
  17.                 this.horizontalScrollPosition = m.horizontalScrollPosition;
  18.             }
  19.         }
  20.        
  21.         public function createMemento():IMemento {
  22.             return new UndoableTextAreaMemento( this.text, this.verticalScrollPosition, this.horizontalScrollPosition );
  23.         }
  24.     }
  25. }

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).

Actionscript:
  1. package com.joangarnet.flash.controls
  2. {
  3.     import com.joangarnet.patterns.memento.IMemento;
  4.    
  5.     public class UndoableTextAreaMemento implements IMemento {
  6.         private var _horizontalScrollPosition:Number;
  7.         private var _verticalScrollPosition:Number;
  8.         private var _text:String;
  9.        
  10.         public function UndoableTextAreaMemento( text:String, verticalScrollPosition:Number, horizontalScrollPosition:Number ) {
  11.             this._verticalScrollPosition = verticalScrollPosition;
  12.             this._horizontalScrollPosition = horizontalScrollPosition;
  13.             this._text = text;
  14.         }
  15.        
  16.         public function get horizontalScrollPosition():Number {
  17.             return _horizontalScrollPosition;
  18.         }
  19.        
  20.         public function get verticalScrollPosition():Number {
  21.             return _verticalScrollPosition;
  22.         }
  23.        
  24.         public function get text():String {
  25.             return _text;
  26.         }
  27.     }
  28. }

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...

Actionscript:
  1. package {
  2.     import com.joangarnet.flash.controls.UndoableTextArea;
  3.     import com.joangarnet.patterns.memento.MementoCaretaker;
  4.    
  5.     import fl.controls.Button;
  6.    
  7.     import flash.display.Sprite;
  8.     import flash.events.Event;
  9.     import flash.events.MouseEvent;
  10.  
  11.     public class MementoSample_1 extends Sprite {
  12.         private var tf:UndoableTextArea;
  13.         private var undo:Button;
  14.         private var redo:Button;
  15.  
  16.         public function MementoSample_1() {
  17.             undo = new Button();
  18.             undo.label = "undo (0)";
  19.             undo.x = 30; undo.y = 25;
  20.             undo.addEventListener( MouseEvent.CLICK, undoHandler );
  21.            
  22.             redo = new Button();
  23.             redo.label = "redo (0)";
  24.             redo.x = undo.x + undo.width + 5; redo.y = 25;
  25.             redo.addEventListener( MouseEvent.CLICK, redoHandler );
  26.  
  27.             tf = new UndoableTextArea();
  28.             tf.x = 30; tf.y = 50;
  29.             tf.width = 200; tf.height = 100;
  30.             tf.addEventListener( Event.CHANGE, changeHandler );
  31.  
  32.             addChild( undo );
  33.             addChild( redo );
  34.             addChild( tf );
  35.         }
  36.  
  37.         private function changeHandler( event:Event ):void {
  38.             MementoCaretaker.getInstance().addMemento( tf, tf.createMemento() );
  39.             printAvailableMementos();
  40.         }
  41.  
  42.         private function undoHandler( event:MouseEvent ):void {
  43.             tf.setMemento( MementoCaretaker.getInstance().undo(tf) );
  44.             printAvailableMementos();
  45.         }
  46.        
  47.         private function redoHandler( event:MouseEvent ):void {
  48.             tf.setMemento( MementoCaretaker.getInstance().redo(tf) );
  49.             printAvailableMementos();
  50.         }
  51.  
  52.         private function printAvailableMementos():void {
  53.             undo.label = "undo (" + MementoCaretaker.getInstance().getUndos(tf).toString() + ")";
  54.             redo.label = "redo (" + MementoCaretaker.getInstance().getRedos(tf).toString() + ")";
  55.         }
  56.     }
  57.  
  58. }

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.

XML.setNotification: escuchando cambios en XML

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:

Actionscript:
  1. /**
  2. * Método que registra una función de callback para que una instancia de XML pueda notificar
  3. * cambios.
  4. * Los tipo de modificaciones que son notificadas a través del callback son:
  5. * - attributeAdded: Al añadir un atributo.
  6. * - attributeChanged: Al modificar un atributo.
  7. * - attributeRemoved: Al eliminar un atributo.
  8. * - nodeAdded: Al añadir un nodo.
  9. * - nodeChanged: Al modificar un nodo.
  10. * - nodeRemoved: Al eliminar un nodo.
  11. * - nameSet: Al asignar el nombre de un nodo.
  12. * - namespaceAdded: Al añadir un espacio de nombres al nodo.
  13. * - namespaceRemoved: Al eliminar un espacio de nombres al nodo.
  14. * - namespaceSet: Al añadir un espacio de nombres al nodo.
  15. * - textSet: Al asignar un nodo text en el nodo.
  16. * @param callback: Referencia a la función callback.
  17. */
  18. function setNotification( callback:Function ):void;

Y un ejemplo:

Actionscript:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="f()">
  3.     <mx:Script>
  4.         <![CDATA[
  5.             import mx.utils.ObjectUtil;
  6.             private function f(){
  7.                 var xml:XML =  
  8.                 <root>
  9.                     <node id="1"/>
  10.                 </root>;
  11.                 xml.setNotification( callback );
  12.                
  13.                 xml.children()[0].@newAttr = "hola";//attributeAdded
  14.                 xml.children()[0].@id = "a";//attributeChanged
  15.                 delete xml.children()[0].@id;//attributeRemoved
  16.                 xml.appendChild( <node1 id="2">Lorem Ipsum</node1> );//nodeAdded
  17.                 delete xml.children()[0];//nodeRemoved
  18.                 (xml.children()[0] as XML).setName("node1Renamed");//nameSet
  19.                 var ns1:Namespace = new Namespace("w1", "http://www.test.com/w1/");
  20.                 var ns2:Namespace = new Namespace("w2", "http://www.test.com/w2/");
  21.                 xml.addNamespace( ns1 );//namespaceAdded
  22.                 xml.addNamespace( ns2 );//namespaceAdded
  23.                 xml.setNamespace( ns1 );//namespaceSet
  24.                 xml.setNamespace( ns2 );//namespaceSet
  25.                 xml.removeNamespace( ns1 );//namespaceRemoved
  26.                 xml.children()[0] = "hola2";//textSet + nodeChanged
  27.                
  28.                 function callback(targetCurrent:Object, command:String, target:Object, value:Object, detail:Object):void{
  29.                     trace( command )
  30.                 }
  31.                
  32.             }
  33.         ]]>
  34.     </mx:Script>
  35. </mx:Application>

UMFlash: de UML a AS3 con StarUML

Hay una nueva herramienta disponible para generación de código AS3 a partir de diagramas de clase UML llamada UMFlash. Está basada en StarUML y ha sido desarrollada por UnderMedia a partir de un trabajo inicial de Digitaldogbyte.
No he tenido oportunidad de probarla porque me acabo de enterar pero seguro que le hecharé un vistazo en cuanto llegue de vacaciones (me voy mañana!).
Para los que quieran adelantarse aquí dejo el artículo de presentación de la herramienta.
Si alguien tiene a oportunidad de pegarle un vistazo feedback será bienvenido!

Strategy pattern

Descripción

A veces es necesario que ciertas funcionalidades en una aplicación sean intercambiables en tiempo de ejecución. Esto es, que en un punto determinado en el ciclo de ejecución de la aplicación se disponga de un grado de flexibilidad que permita ejecutar una misma acción de distintas formas sin que esto implique tener que implementar en dicha acción todas las posibles formas que puedan existir.
El patrón de diseño Strategy define una metodología que permite que esto sea posible de forma sencilla.
En realidad el concepto de Strategy está fuertemente relacionado con una de las bases de la programación orientada a objetos, el polimorfismo: poli(muchas)morfismo(formas), que es lo que permite que una clase pueda tener más de un tipo, el de la misma clase, el de su(s) superclase(s) y el de las interfaces que implementa (si nos ponemos puristas podríamos dejarlo en "las interfaces que implementa" ya que una clase es en realidad una implementación de una interfaz al fin y al cabo, ¿no?).

Implementación

Desarrollando un sencillo ejemplo se verá claramente cómo funciona.
Vamos a poner por ejemplo un sistema de cobro de una tienda virtual. Las tiendas viruales disponen de distintos sistemas de pago: Visa, PayPal, Google Checkout, etc... cada uno con una lógica de conexión a la pasarela de pago diferente. Sinembargo todos los sistemas de pago tiene algo en común, que se paga :)
Con esto ya tenemos todos los ingredientes necesarios para una implementación del patrón Strategy.
Los actores son la clase principal de la aplicación TiendaVirtual, la interfaz común de todos los sistemas de pago ISistemaDePago y finalmente las distintas implementaciones (estrategias) de dicha interfaz SistemaDePagoVisa, SistemaDePagoPayPal y SistemaDePagoGoogleCheckout.
Esto traducido a código sería así:

Actionscript:
  1. // clase de la aplicación
  2. public class TiendaVirtual{
  3.     private var sistemaDePago:ISistemaDePago;
  4.     public function seleccionaSistemaDePago( sistemaDePago:ISistemaDePago ):void{
  5.         this.sistemaDePago = sistemaDePago;
  6.     }
  7.     public function realizaPago():void{
  8.         if( sistemaDePago.pagar() ){
  9.             trace("Pago realizado!");
  10.         }else{
  11.             trace("Pago no realizado...");
  12.         }
  13.     }
  14. }
  15.  
  16. // la interfaz común de todas las "estrategias" de pago
  17. public interface ISistemaDePago{
  18.     function pagar():Boolean;
  19. }
  20.  
  21. // las distinas implementaciones de ISistemaDePago (estrategias)
  22. public class SistemaDePagoVisa implements ISistemaDePago{
  23.     public functino pagar():Boolean{
  24.         trace("Pagando con Visa");
  25.         // lógica de pago...
  26.         return resultadoDelPago;
  27.     }
  28. }
  29. public class SistemaDePagoPayPal implements ISistemaDePago{
  30.     public functino pagar():Boolean{
  31.         trace("Pagando con PayPal");
  32.         // lógica de pago...
  33.         return resultadoDelPago;
  34.     }
  35. }
  36. public class SistemaDePagoGoogleCheckout implements ISistemaDePago{
  37.     public functino pagar():Boolean{
  38.         trace("Pagando con Google Checkout");
  39.         // lógica de pago...
  40.         return resultadoDelPago;
  41.     }
  42. }
  43.  
  44. // un posible uso de la aplicación...
  45. var tienda:TiendaVirtual = new TiendaVirtual();
  46. tienda.seleccionaSistemaDePago( new SistemaDePagoPayPal() );
  47. tienda.realizaPago();

Beneficios

Es limpio y permite abstraer a la aplicación de la lógica de pago.
Los distintos proveedores de pasarelas de pago solo deben desarrollar sus estrategias de pago implementando la interfaz ISistemaDePago sin tener que saber cómo funciona cada sistema de tienda virtual.
Por otro lado se puede ver que si en algún momento se requiere añadir más sistemas de pago lo único que vamos a tener que hacer es añadir en nuestro classpath la nueva implementación de ISistemaDePago y compilar.

Inyección de dependencias

Si nos lo montamos de forma más dinámica para evitar el paso de la compilación incluso se podrían inyectar las dependencias cargándolas en tiempo de ejecución de un swf y accediendo a ellas utilizando las nuevas APIs de Reflection de AS3. Algo así:

Actionscript:
  1. var nombreClase:String = 'SistemaDePagoVisa';
  2. var urlDependencia:URLRequest = new URLRequest( nombreClase+'.swf' );
  3. urlDependencia.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
  4. dependencia = new Loader();
  5. dependencia.addEventListener(EventType.COMPLETE, dependenciaCargada);
  6. dependencia.load( urlDependencia );
  7.  
  8. private function dependenciaCargada(e:Event):Void {
  9.     var referenciaISistemaDePago:Class = dependencia.loaderInfo.applicationDomain.getClass('com.tienda.'+nombreClase);
  10.     var sistemaDePago:ISistemaDePago = new referenciaISistemaDePago() as ISistemaDePago;
  11.     tienda.addSistemaDePago( sistemaDePago );// supongamos que TiendaVirtual tiene este método de inyección de sistemas de pago
  12. }

Conclusión

En cualquiera de los dos casos el beneficio es claro, por un lado no hay necesidad de tocar el corazón de nuestra aplicación con el consiguiente peligro de introducir nuevos bugs, por el otro añadir un punto de abstracción que permite dar flexibilidad a un aspecto potencialmente cambiante.

Adapter pattern

El patrón de diseño Adapter es un sistema que nos permite que, en un momento dado en nuestra aplicación podamos utilizar una clase de un tipo incompatible como si fuera del tipo que realmente se requiere. Es decir, adapta una clase existente de tal forma que pueda ser utilizada en lugares dónde en principio no debería poder utilizarse.
Implementacionalmente el patrón es muy parecido al Decorator, pero conceptualmente cada uno está muy diferenciado del otro, y como que los patrones tienen mucho de éso, de proporcionar un lenguaje común que nos permita entendernos cuándo hablamos del uno o del otro pues es importante saber ver la diferencia.

Siguiendo la tónica del post anterior he buscado un ejemplo muy básico y casi caricaturesco de lo que sería un Adapter.
El ejemplo es el siguiente:

Actionscript:
  1. class ConductorIngles{
  2.     public function drive(){
  3.     }
  4. }
  5.  
  6. class ConductorEspañol{
  7.     public function conducir(){
  8.     }
  9. }
  10.  
  11. // éste el el Adapter
  12. class ConductorInglesAdaptadoAEspaña extends ConductorEspañol{
  13.     private var conductorIngles:ConductorIngles = new ConductorIngles();
  14.     override public function conducir(){
  15.         conductorIngles.drive();
  16.     }
  17. }
  18.  
  19. class CarreteraEspañola{
  20.     public function nuevoConductor( conductor:ConductorEspañol ){
  21.         conductor.conducir();
  22.     }
  23. }
  24.  
  25. var carretera:CarreteraEspañola = new CarreteraEspañola();
  26. carretera.nuevoConductor( new ConductorInglesAdaptadoAEspaña() );

Igual es un poco drástico... pero supongamos que en las carreteras españolas solo pueden conducir ConductorEspañol(es). Si viene un británico de vacaciones y se trae el coche lo lleva crudo, porque las carreteras no están hechas para él. Entonces lo que tiene que hacer el individuo es ponerse un sombrero Mexicano ( si, en España suele pasar que la gente de fuera piensa que llevando uno de ésos se integra más en el folclore local :) ) y así hacerse pasar por un ConductorEspañol.
El sombrero Mexicano en el ejemplo anterior equivale a la clase ConductorInglesAdaptadoAEspaña y nos permite adaptar ConductorIngles para que se pueda utilizar su lógica de conducción; el método drive(); a través del método homólogo en ConductorEspañol; el método conducir().
Solo de este modo podremos añadir un ConductorIngles en una CarreteraEspañola, ya que como se puede ver el método nuevoConductor() espera como parámetro un ConductorEspañol.

Next Page →