Tipos Enumerados en AS3 (I)

19 de noviembre de 2008

En este pequeño artículo vamos a describir un método para la emulación de tipos enumerados (“enums“) en AS3. Los tipos enumerados son de uso habitual en Java, C++ o C#. Básicamente son un tipo de dato, muchas veces definido por el programador, compuesto por una serie de constantes llamadas enumerados. Para ampliar información, se puede consultar el artículo de Wikipedia sobre los tipos enumerados (en inglés).

NOTA:En esta primera parte se expone un primer método que no funciona bien, pero sirve como introducción a la solución correcta. La solución en el artículo Tipos Enumerados en AS3(II)

Uno de los problemas que AS3 presenta es la ausencia de constructores privados. No se puede declarar la función constructora con el modificador “private” porque dará un error de compilación. Debido esta característica muchos patrones e ideas, comunes en otros lenguajes de programación, son difíciles de portar a AS3, o conllevan una solución bastante poco elegante. La implementación del patrón Singleton, por ejemplo, se complica mucho si queremos asegurar que no se pueda crear una instancia del objeto por error. Ya lo explicaremos en otro artículo.

Para los Singleton existe una artimaña muy discutida, que es la utilización de una clase internal, definida en el mismo fichero de la clase. Desgraciadamente esta solución no nos es útil en este caso, ya que el código estático se ejecuta antes de que esta clase esté disponible para ser instanciada.

La mejor manera de solucionar nuestro problema es utilizar un bloque estático, que se ejecuta cuando se carga por primera vez la clase, que bloquee la instanciación sucesiva.

[code lang=”actionscript”]
package net.somms.i8n
{

public class IdiomEnum
{
public static const English:IdiomEnum = new IdiomEnum(“EN”);
public static const Spanish:IdiomEnum = new IdiomEnum(“ES”);
private var lock:Boolean = false;
// Bloque estático de código
{
lock = true;
}

private var value:String;
public function IdiomEnum(Name:String)
{

if(lock)
{
throw new Error(“Esta clase no puede ser creada directamente. Es un enumerado”);
}
this.value = Name;
}

public function toString()
{
return value;
}

}
}
[/code]

En la primera parte del código creamos instancias de nuestra clase y las asignamos a constantes estáticas. Como no se ha ejecutado todavía el bloque de código estático, no hay problema, y el constructor se ejecuta normalmente. A continuación se ejecuta nuestro bloque estático de código, y se activa el bloqueo de la clase.

Esto funciona. Lo único que no cubre esta solución es impedir que el usuario de la clase llame a su constructor en tiempo de diseño. No se obtendrá ningún error hasta que no se ejecute el código, ya que ni siquiera el compilador encontrará problemas.

Intentando heredar

Este código se tendrá que repetir en todos los tipos numerados que creemos. Además de tedioso, nos debe saltar la alarma que lleva cualquier programador en los genes. Esa que salta cuando haces copiar y pegar dos veces del mismo trozo de código. ¿Y si intentamos heredar? Podríamos poner la el código de bloqueo en una clase padre, y que las demás hereden sin enterarse del tema:
[code lang=”actionscript”]
package net.somms.utils
{
public class Enum
{
private var value:String;

protected static var lock:Boolean = false;
{
trace(“Codigo estático de Enum”);
lock = true;
}

public function Enum(Value:String)
{
if(lock)
{
throw new Error(“Esta clase no puede ser creada directamente. Es un enumerado”);
}
trace(“Constructor de Enum ” + Enum.lock);
this.value = Value;
}

public function toString()
{
return value;
}

}
}
[/code]

Y luego crear nuestra clase hija de la siguiente manera:
[code lang=”actionscript”]
package net.somms.i8n
{
import net.somms.utils.Enum;

public class IdiomEnum extends Enum
{
public static const English:IdiomEnum = new IdiomEnum(“EN”);
public static const Spanish:IdiomEnum = new IdiomEnum(“ES”);
{

trace(“Codigo estático de IdiomEnum ” + lock);
}
public function IdiomEnum(Name:String)
{
trace(“Constructor de IdiomEnum”);
super(Name);

}

}
}
[/code]

Si intentamos hacer esto, no funciona. La razón es que el orden de llamadas no es el que nos conviene. Para que esto funcione primero debería ejecutarse el código estático de IdiomEnum, y después el de Enum.

Sin embargo, es justo al revés. He incluido unos cuantos trace para que se vea claro. Así que cuando se llama en IdiomEnum a su constructor para crear las constantes estáticas, la clase ya está bloqueada y se lanza el error. No nos queda más remedio que realizar el bloqueo en la clase hija. Y más nos vale sellarla porque de esta clase no se podrá heredar, ya que tendremos el mismo problema. Nos quedará nuestro código de la siguiente manera:

[code lang=”actionscript”]
package net.somms.utils
{
public class Enum
{
private var value:String;

protected static var lock:Boolean = false;
{
trace(“Codigo estático de Enum”);

}

public function Enum(Value:String)
{
if(lock)
{
throw new Error(“Esta clase no puede ser creada directamente. Es un enumerado”);
}
trace(“Constructor de Enum ” + Enum.lock);
this.value = Value;
}

public function toString()
{
return value;
}

}
}
[/code]
[code lang=”actionscript”]
package net.somms.i8n
{
import net.somms.utils.Enum;

public final class IdiomEnum extends Enum
{
public static const English:IdiomEnum = new IdiomEnum(“EN”);
public static const Spanish:IdiomEnum = new IdiomEnum(“ES”);
{
lock = true;
trace(“Codigo estático de IdiomEnum ” + lock);
}
public function IdiomEnum(Name:String)
{
trace(“Constructor de IdiomEnum”);
super(Name);

}

}
}
[/code]

No es la solución más elegante. Cada vez que heredemos de Enum tenemos que recordar incluir el bloque de código estático que activa el bloqueo de la clase. Visto lo visto, menos da una piedra.

NOTA: Esto NO funciona, ya que la propiedad “lock” pertenece a Enum. Una vez creado IdiomEnum, ¡No podremos crear ningún otro tipo numerado!. La solución en el artículo Tipos Enumerados en AS3(II)

  • qrcode link