Los espacios de nombres ofrecen control sobre la visibilidad de las propiedades y los métodos que se creen. Los especificadores de control de acceso public, private, protected e internal son espacios de nombres incorporados. Si estos especificadores de control de acceso predefinidos no se adaptan a las necesidades del programador, es posible crear espacios de nombres personalizados.
Para los programadores que estén familiarizados con los espacios de nombres XML, gran parte de lo que se va a explicar no resultará nuevo, aunque la sintaxis y los detalles de la implementación de ActionScript son ligeramente distintos de los de XML. Si nunca se ha trabajado con espacios de nombres, el concepto en sí es sencillo, pero hay que aprender la terminología específica de la implementación.
Para comprender mejor cómo funcionan los espacios de nombres, es necesario saber que el nombre de una propiedad o un método siempre contiene dos partes: un identificador y un espacio de nombres. El identificador es lo que generalmente se considera un nombre. Por ejemplo, los identificadores de la siguiente definición de clase son sampleGreeting y sampleFunction():
class SampleCode
{
var sampleGreeting:String;
function sampleFunction () {
trace(sampleGreeting + " from sampleFunction()");
}
}
Siempre que las definiciones no estén precedidas por un atributo de espacio de nombres, sus nombres se califican mediante el espacio de nombres internal predeterminado, lo que significa que sólo estarán visibles para los orígenes de llamada del mismo paquete. Si el compilador está configurado en modo estricto, emite una advertencia para indicar que el espacio de nombres internal se aplica a cualquier identificador que no tenga un atributo de espacio de nombres. Para asegurarse de que un identificador esté disponible en todas partes, hay que usar específicamente el atributo public como prefijo del nombre de identificador. En el ejemplo de código anterior, sampleGreeting y sampleFunction() tienen internal como valor de espacio de nombres.
Se deben seguir tres pasos básicos al utilizar espacios de nombres. En primer lugar, hay que definir el espacio de nombres con la palabra clave namespace. Por ejemplo, el código siguiente define el espacio de nombres version1:
namespace version1;
En segundo lugar, se aplica el espacio de nombres utilizándolo en lugar de utilizar un especificador de control de acceso en una declaración de propiedad o método. El ejemplo siguiente coloca una función denominada myFunction() en el espacio de nombres version1:
version1 function myFunction() {}
En tercer lugar, tras aplicar el espacio de nombres, se puede hacer referencia al mismo con la directiva use o calificando el nombre de un identificador con un espacio de nombres. En el ejemplo siguiente se hace referencia a la función myFunction() mediante la directiva use:
use namespace version1;
myFunction();
También se puede utilizar un nombre completo para hacer referencia a la función myFunction(), como se indica en el siguiente ejemplo:
version1::myFunction();
Definición de espacios de nombres
Los espacios de nombres contienen un valor, el Identificador uniforme de recurso (URI), que a veces se denomina nombre del espacio de nombres. El URI permite asegurarse de que la definición del espacio de nombres es única.
Para crear un espacio de nombres se declara una definición de espacio de nombres de una de las dos maneras siguientes. Se puede definir un espacio de nombres con un URI explícito, de la misma manera que se define un espacio de nombres XML, o se puede omitir el URI. En el siguiente ejemplo se muestra la manera de definir un espacio de nombres mediante un URI:
namespace flash_proxy = "http://www.adobe.com/flash/proxy";
El URI constituye una cadena de identificación única para ese espacio de nombres. Si se omite el URI, como en el siguiente ejemplo, el compilador creará una cadena de identificación interna única en lugar del URI. El usuario no tiene acceso a esta cadena de identificación interna.
namespace flash_proxy;
Una vez definido un espacio de nombres, con o sin URI, ese espacio de nombres no podrá definirse de nuevo en el mismo ámbito. Si se intenta definir un espacio de nombres definido previamente en el mismo ámbito, se producirá un error del compilador.
Si se define un espacio de nombres dentro de un paquete o una clase, el espacio de nombres puede no estar visible para código externo al paquete o la clase, a menos que se use el especificador de control de acceso apropiado. Por ejemplo, el código siguiente muestra el espacio de nombres flash_proxy definido en el paquete flash.utils. En el siguiente ejemplo, la falta de un especificador de control de acceso significa que el espacio de nombres flash_proxy sólo estará visible para el código del paquete flash.utils y no estará visible para el código externo al paquete:
package flash.utils
{
namespace flash_proxy;
}
El código siguiente utiliza el atributo public para hacer que el espacio de nombres flash_proxy esté visible para el código externo al paquete:
package flash.utils
{
public namespace flash_proxy;
}
Aplicación de espacios de nombres
Aplicar un espacio de nombres significa colocar una definición en un espacio de nombres. Entre las definiciones que se puede colocar en espacios de nombres se incluyen funciones, variables y constantes (no se puede colocar una clase en un espacio de nombres personalizado).
Considérese, por ejemplo, una función declarada con el espacio de nombres de control de acceso public. Al utilizar el atributo public en una definición de función se coloca la función en el espacio de nombres public, lo que hace que esté disponible para todo el código. Una vez definido un espacio de nombres, se puede utilizar el espacio de nombres definido de la misma manera que se utilizaría el atributo public y la definición estará disponible para el código que puede hacer referencia al espacio de nombres personalizado. Por ejemplo, si se define un espacio de nombres example1, se puede añadir un método denominado myFunction() utilizando example1 como un atributo, como se indica en el siguiente ejemplo:
namespace example1;
class someClass
{
example1 myFunction() {}
}
Si se declara el método myFunction() con el espacio de nombres example1 como un atributo, significa que el método pertenece al espacio de nombres example1.
Se debe tener en cuenta lo siguiente al aplicar espacios de nombres:
Sólo se puede aplicar un espacio de nombres a cada declaración.
No hay manera de aplicar un atributo de espacio de nombres a más de una definición simultáneamente. Es decir, si se desea aplicar el espacio de nombres a diez funciones distintas, se debe añadir el espacio de nombres como un atributo a cada una de las diez definiciones de función.
Si se aplica un espacio de nombres, no se puede especificar también un especificador de control de acceso, ya que los espacios de nombres y los especificadores de control de acceso son mutuamente excluyentes. Es decir, no se puede declarar una función o propiedad como public, private, protected o internal y aplicar también el espacio de nombres.
Referencia a un espacio de nombres
No es necesario hacer referencia explícita a un espacio de nombres al usar un método o una propiedad declarados con alguno de los espacios de nombres de control de acceso, como public, private, protected e internal. Esto se debe a que el acceso a estos espacios de nombres especiales se controla por el contexto. Por ejemplo, las definiciones colocadas en el espacio de nombres private estarán disponibles automáticamente para el código de la misma clase. Sin embargo, para los espacios de nombres personalizados que se definan no existirá este control mediante el contexto. Para poder utilizar un método o una propiedad que se ha colocado en un espacio de nombres personalizado, se debe hacer referencia al espacio de nombres.
Se puede hacer referencia a espacios de nombres con la directiva use namespace o se puede calificar el nombre con el espacio de nombres mediante el signo calificador de nombre (::). Al hacer referencia a un espacio de nombres con la directiva use namespace “se abre” el espacio de nombres, de forma que se puede aplicar a cualquier identificador que no esté calificado. Por ejemplo, si se define el espacio de nombres example1, se puede acceder a nombres de ese espacio de nombres mediante use namespace example1:
use namespace example1;
myFunction();
Es posible abrir más de un espacio de nombres simultáneamente. Cuando se abre un espacio de nombres con use namespace, permanece abierto para todo el bloque de código en el que se abrió. No hay forma de cerrar un espacio de nombres explícitamente.
Sin embargo, si hay más de un espacio de nombres abierto, aumenta la probabilidad de que se produzcan conflictos de nombres. Si se prefiere no abrir un espacio de nombres, se puede evitar la directiva use namespace calificando el nombre del método o la propiedad con el espacio de nombres y el signo calificador de nombre. Por ejemplo, el código siguiente muestra la manera de calificar el nombre myFunction() con el espacio de nombres example1:
example1::myFunction();
Uso de espacios de nombres
Puede encontrar un ejemplo real de un espacio de nombres que se utiliza para evitar conflictos de nombre en la clase flash.utils.Proxy que forma parte de ActionScript 3.0. La clase Proxy, que es la sustitución de la propiedad Object.__resolve de ActionScript 2.0, permite interceptar referencias a propiedades o métodos no definidos antes de que se produzca un error. Todos los métodos de la clase Proxy residen en el espacio de nombres flash_proxy para evitar conflictos de nombres.
Para entender mejor cómo se utiliza el espacio de nombres flash_proxy, hay que entender cómo se utiliza la clase Proxy. La funcionalidad de la clase Proxy sólo está disponible para las clases que heredan de ella. Es decir, si se desea utilizar los métodos de la clase Proxy en un objeto, la definición de clase del objeto debe ampliar la clase Proxy. Por ejemplo, si desea se interceptar intentos de llamar a un método no definido, se debe ampliar la clase Proxy y después reemplazar el método callProperty() de la clase Proxy.
La implementación de espacios de nombres es generalmente un proceso en tres pasos consistente en definir y aplicar un espacio de nombres, y después hacer referencia al mismo. Sin embargo, como nunca se llama explícitamente a ninguno de los métodos de la clase Proxy, sólo se define y aplica el espacio de nombres flash_proxy, pero nunca se hace referencia al mismo. ActionScript 3.0 define el espacio de nombres flash_proxy y lo aplica en la clase Proxy. El código sólo tiene que aplicar el espacio de nombres flash_proxy a las clases que amplían la clase Proxy.
El espacio de nombres flash_proxy se define en el paquete flash.utils de una manera similar a la siguiente:
package flash.utils
{
public namespace flash_proxy;
}
El espacio de nombres se aplica a los métodos de la clase Proxy, como se indica en el siguiente fragmento de dicha clase:
public class Proxy
{
flash_proxy function callProperty(name:*, ... rest):*
flash_proxy function deleteProperty(name:*):Boolean
...
}
Como se indica en el código siguiente, se debe importar primero la clase Proxy y el espacio de nombres flash_proxy. A continuación se debe declarar la clase de forma que amplíe la clase Proxy (también se debe añadir el atributo dynamic si se compila en modo estricto). Al sustituir el método callProperty(), se debe utilizar el espacio de nombres flash_proxy.
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
dynamic class MyProxy extends Proxy
{
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
}
Si se crea una instancia de la clase MyProxy y se llama a un método no definido, como el método testing() llamado en el ejemplo siguiente, el objeto Proxy intercepta la llamada al método y ejecuta las sentencias del método callProperty() sustituido (en este caso, una simple sentencia trace()).
var mySample:MyProxy = new MyProxy();
mySample.testing(); // method call intercepted: testing
Tener los métodos de la clase Proxy dentro del espacio de nombres flash_proxy ofrece dos ventajas. En primer lugar, tener un espacio de nombres independiente reduce el desorden en la interfaz pública de cualquier clase que amplíe la clase Proxy. (Hay aproximadamente una docena de métodos en la clase Proxy que se pueden sustituir; todos ellos han sido diseñados para no ser llamados directamente. Colocarlos todos en el espacio de nombres public podría provocar confusiones.) En segundo lugar, el uso del espacio de nombres flash_proxy evita los conflictos de nombres en caso de que la subclase de Proxy contenga métodos de instancia con nombres que coincidan con los de los métodos de la clase Proxy. Por ejemplo, supongamos que un programador desea asignar a uno de sus métodos el nombre callProperty(). El código siguiente es aceptable porque su versión del método callProperty() está en un espacio de nombres distinto:
dynamic class MyProxy extends Proxy
{
public function callProperty() {}
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
Los espacios de nombres también pueden ser útiles cuando se desea proporcionar acceso a métodos o propiedades de una manera que se no puede realizar con los cuatro especificadores de control de acceso (public, private, internal y protected). Por ejemplo, un programador puede tener algunos métodos de utilidad repartidos por varios paquetes. Desea que estos métodos estén disponibles para todos los paquetes, pero no quiere que sean públicos. Para ello, se puede crear un espacio de nombres y utilizarlo como un especificador de control de acceso especial.
En el ejemplo siguiente se utiliza un espacio de nombres definido por el usuario para agrupar dos funciones que residen en paquetes distintos. Al agruparlos en el mismo espacio de nombres, se puede hacer que ambas funciones estén visibles para una clase o un paquete mediante una única sentencia use namespace.
En este ejemplo se utilizan cuatro archivos para ilustrar la técnica. Todos los archivos deben estar en la ruta de clases. El primer archivo, myInternal.as, se utiliza para definir el espacio de nombres myInternal. Como el archivo está en un paquete denominado example, se debe colocar en una carpeta denominada example. El espacio de nombres se marca como public de forma que se pueda importar en otros paquetes.
// myInternal.as in folder example
package example
{
public namespace myInternal = "http://www.adobe.com/2006/actionscript/examples";
}
Los archivos Utility.as y Helper.as definen las clases que contienen los métodos que deben estar disponibles para otros paquetes. La clase Utility está en el paquete example.alpha, lo que significa que se debe colocar el archivo dentro de una subcarpeta de la carpeta example denominada alpha. La clase Helper está en el paquete example.beta, por lo que se debe colocar el archivo dentro de otra subcarpeta de la carpeta example denominada beta. Ambos paquetes, example.alpha y example.beta, deben importar el espacio de nombres antes de utilizarlo.
// Utility.as in the example/alpha folder
package example.alpha
{
import example.myInternal;
public class Utility
{
private static var _taskCounter:int = 0;
public static function someTask()
{
_taskCounter++;
}
myInternal static function get taskCounter():int
{
return _taskCounter;
}
}
}
// Helper.as in the example/beta folder
package example.beta
{
import example.myInternal;
public class Helper
{
private static var _timeStamp:Date;
public static function someTask()
{
_timeStamp = new Date();
}
myInternal static function get lastCalled():Date
{
return _timeStamp;
}
}
}
El cuarto archivo, NamespaceUseCase.as, es la clase principal de la aplicación, y debe estar en el mismo nivel que la carpeta example. En Flash Professional, esta clase se utilizaría como la clase de documento para el archivo FLA. La clase NamespaceUseCase también importa el espacio de nombres myInternal y lo utiliza para llamar a los dos métodos estáticos que residen en los otros paquetes. El ejemplo utiliza métodos estáticos sólo para simplificar el código. Se pueden colocar tanto métodos estáticos como métodos de instancia en el espacio de nombres myInternal.
// NamespaceUseCase.as
package
{
import flash.display.MovieClip;
import example.myInternal; // import namespace
import example.alpha.Utility;// import Utility class
import example.beta.Helper;// import Helper class
public class NamespaceUseCase extends MovieClip
{
public function NamespaceUseCase()
{
use namespace myInternal;
Utility.someTask();
Utility.someTask();
trace(Utility.taskCounter); // 2
Helper.someTask();
trace(Helper.lastCalled); // [time someTask() was last called]
}
}
}