« Octubre 2006 | Inicio | Diciembre 2006 »

Noviembre 27, 2006

Enhorabuena a los premiados

A Sergio, a Bea, a Javi, a Miguel. El equipo de animación de mi anterior empresa. La última semana que estuve allí, prepararon una pieza para el certamen de notodo anímate 2006. Hoy lunes 27 ha salido el listado de premiados y ..... oeoeoeoeoeoe 1er premio.
Lo dicho, enhorabuena a los premiados ( el fabuloso corto "Opel Troya" se puede ver desde http://www.notodo.com/animate/ , no se lo pierdan que está muy chulo ).

opel troya, corto ganador

Noviembre 21, 2006

Novedades de AS3 (III): Modificadores de acceso

Los modificadores de acceso en AS2 eran bastante claros: una variable o un método solo podían ser públicos o privados. Otra cosa es que luego fueran realmente privados o no, pero eso es otra historia.

Uno de los cambios en la estructura de las clases AS3 es la existencia de un idiom para los modificadores de acceso más cercano al de otros lenguajes, como Java, incluyendo dos nuevos modificadores: internal y protected.

internal

Es el modificador por defecto. Los métodos y variables de una clase declarados como internal son accesibles a todo el package al que pertenece esa clase.

Para comprobarlo, construye una clase con un método internal:

package internalTest
{
	import flash.util.trace
	
	public class HasInternalMethod
	{
		function HasInternalMethod( )
		{
			trace( "HasInternalMethod.constructor" );
		}
		
		internal function internalMethod( )
		{
			trace( "HasInternalMethod.internalMethod( )" );
		}
	}
}

A continuación, en el mismo package, otra clase con un método público, que instancie la clase anterior, y que intente acceder al método internal de esa instancia:

package internalTest
{
	import flash.util.trace
	
	public class HasPublicMethod
	{
		function HasPublicMethod( )
		{
			trace( "HasPublicMethod.constructor" );
		}
		
		public function publicMethod( )
		{
			var internalInstance: HasInternalMethod = new HasInternalMethod( );
			internalInstance.internalMethod( );
		}
		
	}
	
}

A continuación, en la línea de tiempo principal, puedes comprobar que no se puede acceder directamente al método internal:

import internalTest.HasInternalMethod

var instance: HasInternalMethod = new HasInternalMethod( );

instance.internalMethod( );

Pero sí a través del método público de la segunda clase:

import internalTest.HasPublicMethod

var instance: HasPublicMethod = new HasPublicMethod( );

instance.publicMethod( );

protected

Los métodos y variables de una clase declarados como protected sólo son accesibles a las subclases. No al resto del paquete, ni a los públicos.

Sin embargo, un método declarado como protected también será visible para subclases de la original que no estén en el mismo paquete.

Para comprobarlo, declara una clase con un método protected:


package protectedTest
{
import flash.util.trace

public class HasProtectedMethod
{
function HasProtectedMethod( )
{
trace( "HasProtectedMethod.constructor" );
}

internal function internalMethod( )
{
trace( "HasProtectedMethod.internalMethod( )" );
}

protected function superMethod( )
{
trace( "HasProtectedMethod.superMethod" );
}
}
}

Y una subclase de ésta, que intente atacar a ese método protected de la superclase:

package protectedTest
{
	public class SubClass extends HasProtectedMethod
	{
		function SubClass( )
		{
			trace( "Subclass.constructor" );
		}
		
		public function init( )
		{
			//Método internal de la superclase
			internalMethod( );
			
			//Método protected de la superclase
			superMethod( );
		}
	}
}

A continuación, crea una instancia de la subclase en la línea de tiempo principal:

import protectedTest.*

var instance: SubClass = new SubClass( );

instance.init( );

Sin embargo, no puedes acceder directamente al método de la superclase desde fuera del package:

var superInstance: HasProtectedMethod = new HasProtectedMethod( );
superInstance.superMethod( );

Eso generará el correspondiente error de compilación.

private

Private, ahora, quiere decir "totalmente privado". Los métodos y variables declarados como privados no pueden ser accesibles ni por otras clases del mismo package, ni siquiera por las subclases.

Declara una clase con un método privado:

package privateTest
{
	import flash.util.trace
	
	public class HasPrivateMethod
	{
		function HasPrivateMethod( )
		{
			trace( "HasPrivateMethod.constructor" );
		}
		
		internal function internalMethod( )
		{
			trace( "HasPrivateMethod.internalMethod( )" );
		}
		
		protected function superMethod( )
		{
			trace( "HasPrivateMethod.superMethod" );
		}
		
		private function privateMethod( )
		{
			trace( "HasPrivateMethod.privateMethod" );
		}
	}
}

A continuación, una subclase de ésta, que intente lanzar un mensaje al método privado de la superclase:

package privateTest
{
	public class SubClass extends HasPrivateMethod
	{
		function SubClass( )
		{
			trace( "Subclass.constructor" );
		}
		
		public function init( )
		{
			//Método internal de la superclase
			internalMethod( );
			
			//Método protected de la superclase
			superMethod( );
			
			//Método private de la superclase. Error de compilación
			privateMethod( );
			
		}
	}
}

A continuación, desde la línea de tiempo principal, lanza el ejemplo:

import privateTest.*

var instance: SubClass = new SubClass( );

instance.init( );

El uso consciente de private y protected permite un control muy fino sobre el encapsulamiento de las superclases. De esa forma, uno de los problemas de la herencia, que era la exposición de la superclase a las subclases puede resolverse declarando métodos como privados.

override

No es posible sobrescribir variables declaradas en una superclase, sean var o const. Sin embargo, sí se pueden sobrescribir métodos. No obstante, sólo se pueden sobrescribir métodos que no sean estáticos (ya que los estáticos no se heredan), que no se hayan declarado como final, y que no se hayan declarado como private.

Por ejemplo, la superclase podría ser:

package overrideTest
{
	import flash.util.trace
	
	public class HasOverrideMethod
	{
		function HasOverrideMethod( )
		{
			trace( "HasOverrideMethod.constructor" );
		}
		
		internal function internalMethod( )
		{
			trace( "HasOverrideMethod.internalMethod( )" );
		}
		
		protected function superMethod( )
		{
			trace( "HasOverrideMethod.superMethod" );
		}
		
		private function privateMethod( )
		{
			trace( "HasFinalMethod.privateMethod" );
		}
		
		final function finalMethod( )
		{
			trace( "HasOverrideMethod.finalMethod" );
		}
		
		protected function toOverride( itemCount: Number, address: String ): String
		{
			return "";
		}
	}
}

Y la subclase:

package overrideTest
{
	public class SubClass extends HasOverrideMethod
	{
		function SubClass( )
		{
			trace( "Subclass.constructor" );
		}
		
		public function init( )
		{
			//Método internal de la superclase
			internalMethod( );
			
			//Método protected de la superclase
			superMethod( );
			
			//Método private de la superclase
			//privateMethod( );
			
		}
		
		//No compila, distinto tipo de retorno
		/*
		override protected function toOverride( ): Number
		{
			return 2;
		}
		*/
		
		//No compila, distinto número de parámetros
		/*
		override protected function toOverride( ): String
		{
			return "";
		}
		*/
		
		override protected function toOverride( itemCount: Number, address: String ): String
		{
			return "";
		}		
		
		
		//Este método ocupa el lugar del privado de la superclase
		
		function privateMethod( )
		{
			trace( "método privado de la subclase" );
		}
	}
}

Y para realizar la comprobación:

import overrideTest.*

var instance: SubClass = new SubClass( );

instance.init( );

Hay que tener en cuenta que el método de la subclase debe tener los mismos modificadores de acceso que el de la superclase, el mismo número de parámetros y del mismo tipo, y si devuelve algún valor, debe ser del mismo tipo que el de la clase base. Por ejemplo, si el método de la superclase se declara como protected, el sobrescrito no puede declararse como internal

Si en la superclase se declara algún método como private, al no estar éste disponible para las subclases, se podrían declarar en las subclases métodos con el mismo nombre, sin peligro de colisión, como se puede ver en el ejemplo anterior.

final

La posibilidad de declarar métodos como final es nueva para ActionScript, pero no para muchos otros lenguajes.

Puedes declarar una clase con un método final:

package finalTest
{
	import flash.util.trace
	
	public class HasFinalMethod
	{
		function HasFinalMethod( )
		{
			trace( "HasFinalMethod.constructor" );
		}
		
		internal function internalMethod( )
		{
			trace( "HasFinalMethod.internalMethod( )" );
		}
		
		protected function superMethod( )
		{
			trace( "HasPrivateMethod.superMethod" );
		}
		
		private function privateMethod( )
		{
			trace( "HasFinalMethod.privateMethod" );
		}
		
		final function finalMethod( )
		{
			trace( "HasFinalMethod.finalMethod" );
		}
	}
}

E intentar sobreescribirlo en una subclase:

package finalTest
{
	public class SubClass extends HasFinalMethod
	{
		function SubClass( )
		{
			trace( "Subclass.constructor" );
		}
		
		public function init( )
		{
			//Método internal de la superclase
			internalMethod( );
			
			//Método protected de la superclase
			superMethod( );
			
			//Método private de la superclase
			//privateMethod( );
			
			//Método final de la superclase
			finalMethod( );
		}
		
		override final function finalMethod( )
		{
			
		}
	}
}

Con el correspondiente error de compilación:

import finalTest.*

var instance: SubClass = new SubClass( );

instance.init( );

Las clases también pueden declararse como final para, de esa forma, evitar que puedan ser extendidas:

package finalTest
{
	import flash.util.trace
	
	public final class FinalClass
	{
		function FinalClass( )
		{
			trace( "FinalClass.constructor" );
		}
	}
}

Y su subclase:

package finalTest
{
	import flash.util.trace
	
	public class FinalSubClass extends FinalClass
	{
		function FinalSubClass( )
		{
			trace( "FinalSubClass.constructor" );
		}
	}
}

Y comprobar que no se puede compilar:

import finalTest.*

var instance: FinalSubClass = new FinalSubClass( );

dynamic

Las clases declaradas como dynamic pueden ser modificadas en tiempo de ejecución, se las puede añadir métodos y propiedades. En realidad esto no es una novedad en sí, la verdadera novedad está en que ya no es el comportamiento por defecto

La clase dinámica más sencilla posible seria algo como:

package dynamicTest
{
	public dynamic class DynamicClass
	{
		function DynamicClass( )
		{
			
		}
	}
}

Se puede comprobar que es posible crear propiedades de esa clase en tiempo de ejecución:

import dynamicTest.*

var instance: DynamicClass = new DynamicClass( );

instance[ "var1" ] = 24;

trace( "valor obtenido " + instance.var1 );

En el próximo post ya abandonaremos el terreno de lo abstracto y empezaremos a construir una prueba de concepto, más que aplicación, basada en el modelo-vista-controlador.

El código de los ejemplos puede descargarse aquí.

Noviembre 16, 2006

Leopard Developer Tools Overview

Apple acaba de publicar (creo) en la ADC un extenso artículo en el que se repasan las novedades de las herramientas de desarrollo para Mac OS X que estarán incluídas en la próxima versión del sistema operativo.

Va a ser una larga espera...

Leopard Developer Tools Overview

Noviembre 15, 2006

Sobre el "cambio de contexto"

Joel Spolsky comenta un post de Dimitri Zimini sobre los retrasos que puede suponer sobre un proyecto el sacar al programador del mismo, y hacerle perder toda relación con el mismo, para volver atrás a otro proyecto de hace meses.

Los programadores necesitamos concentrarnos en lo que estamos haciendo. Algunos lo conseguimos aislándonos del exterior con unos auriculares, otros simplemente cerrando la puerta de la sala en la que están trabajando, otros ignorando el teléfono. Cada uno tiene su forma de atacar el problema, pero todos necesitamos lo mismo: tranquilidad.

Tranquilidad para lanzarnos a la resolución de un problema, para alcanzar el estado mental en el que todo tu cerebro, toda tu capacidad lógica, todo tu poder de raciocinio está dedicado única y exclusivamente a una cosa: resolver el bug o desarrollar la funcionalidad.

Pero alcanzar ese estado necesita de un proceso de inmersión que no es trivial, ni en tiempo, ni en esfuerzo. Por eso las interrupciones no suelen ser sólo breves cambios de contexto, sino que en realidad suponen la explosión de una burbuja, la burbuja en la que te encuentras, que te aísla de todo lo que no sea tu funcionalidad.

Por eso, una interrupción de sólo unos minutos te puede suponer una pérdida de horas de trabajo. Porque no todo es tan fácil como poner una señal en un libro para saber dónde te llegas. Es muy complicado salir de un problema para volver a entrar en él, sobre todo si entre medias tienes que realizar otra inmersión en un problema totalmente distinto a aquél del que te han sacado.

Las interrupciones pueden ser inevitables. Hay veces en las que es necesario pararlo todo para resolver un bug de alguna aplicación que se desarrolló hace meses, pero eso no significa que esa deba ser la forma normal de operar.

No se puede estar cambiando de contexto cada hora. Simplemente, no se puede.

Noviembre 14, 2006

Novedades de AS3 (II): Cambios en la declaración de variables y clases. Paquetes.

La transición de ActionScript 2.0 a ActionScript 3.0 no supondrá un cambio como el ocurrido en la sintaxis del lenguaje con el paso de Flash 4 a Flash 5.

Sin embargo, hay una serie de consideraciones a tener en cuenta a la hora de escribir los programas, sobre todo en lo referente a las declaraciones de clases.

Organización del código fuente
Si en ActionScript 2 era obligatorio que en un archivo as estuviera declarada una clase (y sólo una), del mismo nombre que el archivo, en AS3 se puede declarar más de una entidad en un solo fichero. Al menos una de las clases o interfaces incluídos en el package debe ser declarada como pública, y esa clase declarada como púbica debe tener el mismo nombre que el fichero.

Por ejemplo, se puede construir un fichero llamado Test.as con los siguientes contenidos:

package
{
	import flash.util.trace

	public class Test
	{
		function Test()
		{
			trace( "Test.constructor" );
		}
	}

	class Test2
	{
		function Test2( )
		{
			trace( "Test2.constructor" );
		}
	}	
}

y en la línea de tiempo principal de un fla guardado en la misma carpeta que el as anterior:

var ex1: Example1 = new Example1( );
var ex2: Example2 = new Example2( );

Packages

En un package se pueden declarar también variables, funciones, o ejecutar sentencias. Esas variables o funciones deberán declararse con el accessor public o internal (los accesors los veremos en otro post). Prueba a compilar el siguiente código:

package
{
	import flash.util.trace

	var str: String = "Hola, mundo";

	public class SecondTest
	{
		function SecondTest( )
		{
			trace( str );			
		}
	}
}

y en la línea de tiempo principal de tu fla, instancia esa clase:

var second: SecondTest = new SecondTest( );

Los nombres de los packages pueden estar anidados, de forma que los ficheros en los que se declaran deberán estar situados en la ruta en disco correspondiente:

package net.designnation.blog
{
	import flash.util.trace

	public class ThirdTest
	{
		function ThirdTest( )
		{
			trace( "Hola mundo, soy una clase cualificada" );
		}
	}	
}

que puede instanciarse desde la línea de tiempo principal del fla:

import net.designnation.blog.ThirdTest
var third: ThirdTest = new ThirdTest( );

Esa clase deberá eestar localizada en disco en la ruta declarada en el nombre del package:

Captura As3 2 1

Para utilizar un paquete en otro diferente, debe importarse el primero. En los ejemplos anteriores hemos visto cómo para sacar trazas es necesario importar flash.util.trace. Al contrario que en AS2, no se puede utilizar una clase sin importarla, intentando instanciarla usando su nombre cualificado. Aunque se quiera utilizar el nombre cualiificado de una clase para instanciarla, en AS3 sigue siendo necesario realizar el import.

Voy a utilizar la clase del ejemplo anterior, intentando instanciarla desde un package diferente. Para ello, voy a declarar una segunda clase:

package net.designnation.fotoblog
{
	import flash.util.trace

	public class FotoblogTest 
	{
		function FotoblogTest()
		{
			trace( "FotoblogTest.constructor" );
			var thirdTest: net.designnation.blog.ThirdTest = new net.designnation.blog.ThirdTest( );
		}
	}
}

Si intento crear una instancia de esa clase, desde la línea de iempo principal del fla:

import net.designnation.fotoblog.FotoblogTest
var fbTest: FotoblogTest = new FotoblogTest();

Obtendré un error:

**Error** /Users/ctarda/Documents/actionScript/as3/postis_2/net/designnation/fotoblog/FotoblogTest.as : Line 10, Column 41 : [Compiler] Error #1046: Type was not found or was not a compile-time constant: ThirdTest.

			var thirdTest: net.designnation.blog.ThirdTest = new net.designnation.blog.ThirdTest( );

ReferenceError: Error #1065: No se ha definido la variable Timeline0_f0cb1432736611dba99101124d4186e.

Sin embargo, si modifico el código de la clase FotoblogTest, añadiendo el import correspodiente:

package net.designnation.fotoblog
{
	import flash.util.trace
	import net.designnation.blog.ThirdTest

	public class FotoblogTest 
	{
		function FotoblogTest()
		{
			trace( "FotoblogTest.constructor" );
			var thirdTest: net.designnation.blog.ThirdTest = new net.designnation.blog.ThirdTest( );
		}
	}
}

y vuelo a compilar, todo funciona correctamente. En realidad, puedo dejar el constructor de FotoblogTest de la siguiente forma:

function FotoblogTest()
{
	trace( "FotoblogTest.constructor" );
	var thirdTest: ThirdTest = new ThirdTest( );
}

Namespaces

El concepto de namespace es un poco más complejo, y no tiene nada que ver con los namespaces de otros lenguajes.

Los namespaces permiten declarar subconjuntos de propiedades y métodos de una clase, para poder decidir, en tiempo de eejecución, cuáles de esos métodos son accesibles. En el fondo, tanto los accessors como los packages pueden verse como ejemplos concretos de Namespaces.

Como siempre, lo más ilustrativo debería ser un ejemplo:

package
{
	import flash.util.trace	

	namespace FirstNameSpace;
	namespace SecondNameSpace;

	public class Spaced
	{
		function Spaced( )
		{
		trace( "Spaced.constructor" );			
		}		

		FirstNameSpace function myMethod( )
		{
			trace( "Spaced.myMethod - FirstNameSpace" );
		}

		SecondNameSpace function myMethod( )
		{
			trace( "Spaced.myMethod - SecondNameSpace" );
		}		
	}
}

Y en la línea de tiempo principal:

var spaced: Spaced = new Spaced( );
spaced.FirstNameSpace::myMethod( );
spaced.SecondNameSpace::myMethod( );

Lo que debe tenerse en cuenta es que sólo se puede asignar un namespace a una declaración, y que cualquier definición cualificada por un namespace no puede tener asignado también un accessor, ya que son mutuamente excluyentes.

¿La utilidad? La primera utilidad evidente es la de poder modificar el interfaz de una clase en tiempo de ejecución. También es útil para asegurar el acceso a métodos concretos, normalmente distribuídos entre varias clases, sin hacerlos públicos.

El siguiente paso será repasar los modificadores de acceso a clases, métodos y propiedades.

Noviembre 06, 2006

Novedades de AS3 (I)

Comenzamos, cargados de buenas intenciones, una serie sobre las novedades en la nueva iteración del lenguaje de programación asociado a Flash, ActionScript.

Codigo As3

ActionScript ha evolucionado muy rápidamente en los últimos años, pasando de ser un lenguaje de script que permitía realizar algunas manipulaciones básicas sobre objetos gráficos, a un lenguaje de alto nivel orientado a objetos, basado en el estándar ECMAScript 4, con el que se pueden construir aplicaciones web de gran complejidad, basando el desarrollo en las metodologías comunes al resto del desarrollo de software.

Comenzamos hoy, pues, con un rápido repaso, a vista de pájaro, por las novedades en esta revisión del lenguaje, novedades y cambios que iremos desarrollando con más detalle y con ejemplos que esperamos sean suficientemente ilustrativos.

La primera novedad a resaltar, sobre todo por el cambio que puede suponer para muchos en la forma de programar en ActionScript es que, por fin, las clases son selladas por defecto. Por tanto, ya no se pueden crear en tiempo de ejecución métodos ni propiedades de una clase (que es lo que siempre se había hecho, por ejemplo, al crear variables en el ámbito de un mmovieclip), salvo que esa clase se declare explícitamente como dinámica. La principal ventaja de esta arquitectura es que la comprobación de tipos se puede hacer en tiempo de compilación, en vez de en tiempo de ejecución, por lo que se gana, por un lado en seguridad, ya que si hay alguna asignación de tipos incorrecta va a ser detectada en tiempo de compilación, por otro, se gana en agilidad en ejecución, ya que no hay que ir comprobando continuamente que los tipos coinciden, y finalmente se disminuye el consumo de memoria, ya que no es necesario mantener una estructura dinámica en memoria (por ejemplo un hashmap) por cada clase con todos sus métodos y propiedades. Huelga decir, por supuesto, que esos dos son sólo argumentos de apoyo a la verdadera razón de la existencia de las clases selladas: el encapsulamiento, la abstracción de datos. Sobre todo si se deja el control al desarrollador sobre ese encapsulamiento.

El especificador de acceso por defecto para métodos y propiedades ha cambiado. Ya no es public, sino internal, equivalente al protected de Java. Un cambio lógico y necesario, ya que no tiene mucho sentido el construir clases, entidades supuestamente encapsuladas, cuyo modificador de acceso por defecto rompe ese encapsulamiento.

Nuevos tipos. Si una de las pretensiones con las que se plantea la nueva versión del lenguaje es la de mejorar la performance, se hace necesario el implementar nuevos tipos de datos numéricos. Ahora ya hay int (enteros) y Number (float).

La estructura de los ficheros que contienen las declaraciones de las clases también ha cambiado. Al igual que en otros muchos lenguajes, se puede declarar más de una clase en un único fichero, aunque con una condición: al menos una de ellas ha de estar declarada como pública, y debe tener el mismo nombre que el fichero.

Flash 9

También se ha reorganizado toda la API del player, de forma que la organización por paquetes es mucho más lógica y racional.

El sistema de eventos también se normaliza, y se estructura alrededor de una clase base, siguiendo el sistema de eventos del DOM3. Todos los eventos que se quieran emitir, se deberán modelar según la nueva estructura.

Y por fin, hay una nueva arquitectura para representar los elementos gráficos, tanto en lo referente a las entidades que se pueden utilizar para representarlas, como en la forma de hacer efectiva su presentación gráfica. Por fin, los gráficos se desacoplan de la posible lógica asociada a los mismos (la clase MovieClip, por ejemplo, lleva agregado su gráfico, sin ser parte intrínseca de ella), y se crean entidades para cada tipo de gráfico (Graphic, Sprite. etc). La forma de mostrarlas en pantalla sigue siendo similar a la anterior, aunque conceptualmente es muy diferente. La estructura de un swf ya no está formada por movieclips creado dentro de movieclips de forma recursiva, sino que se basa en una clase llamada DisplayList, que implementa un estructura similar a la del DOM html.

Hay mucho más. Delegates, Overrride, XML, enclosures, reflection, bucles for in. Muchas cosas como para tratarlas en un único post.

Por eso, vamos a ir construyendo paso a paso una pequeña aplicación (un reloj basado en el Modelo-Vista-Controlador), que nos permitirá ir conociendo, una por una, muchas de las novedades del lenguaje. Para ello, utilizaremos la Alpha Pública de Flash Professional 9, disponible en Adobe Labs.

Intentaremos, plantear el desarrollo de la forma más neutra, sin crear dependencias ni vinculaciones, en lo posible, con características específicas de la forma de hacer las cosas en Flash, como las root custom class.

Pero eso será en próximos posts.