My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Namespace  
the power of namespaces in AS3.
Code, AS3
Updated Jan 10, 2010 by zwetan

Introduction

One of the most powerfull feature of the AS3 language and yet totally underused (to not say ignored).

The definition given in the language specification is not very clear at first, so let's try to make it simpler.

The idea of the namespaces got their origin from XML namespaces, but really we don't care about that.

The basic principle of those namespaces is to allow you to define your own attribute statements.

what ?

You do know those basic little things like public, private, protected ?

in short defining your own namespace allow you to define your own protected attribute keyword,
if you use it, whatever you define under it will be visible, if you don't use it, it will be simply invisible.

Yeah it's a bit magic like that.

To put it another way, if you define a namespace hello
it can allow you to specify a variable, constant or method under this namespace
and make them accessible (or visible) only if you open the namespace.

And you can create as many namespaces as you want.
And decide to use them all at the same time or not.

Details

The problem with namespaces is there are not that obvious to use, but no worries here some examples and tips.

The thing to remember mainly is if with public, private, protected
the language control the visibility of the defintions,
with your own namespaces, you are in control
but you gonna have to explicitly announce it to the language.

basic usage

Let's say you have a basic class with a method that you want to do different things wether you're in debug or release mode.

package test
{
	public class ClassA
	{
		public function ClassA()
		{
			
		}
		
		public function debugTest( msg:String ):void
		{
			trace( "debug: " + msg );
		}
		
		public function releaseTest( msg:String ):void
		{
			trace( "release: " + msg );
		}
	}
}

not very sexy or usable like that, you just want to use test in either one context or another

first define your namespaces

package test
{
	public namespace debug;
}
package test
{
	public namespace release;
}

and now use them in your class

package test
{
	public class ClassA
	{
		public function ClassA()
		{
			
		}
		
		debug function test( msg:String ):void
		{
			trace( "debug: " + msg );
		}
		
		release function test( msg:String ):void
		{
			trace( "release: " + msg );
		}
	}
}

yes you just have 2 methods named the same, and yes you just replaced your public attributes
by 2 custom attributes debug and release.

now let's use this

import test.ClassA;

var a:ClassA = new ClassA();
       a.test( "hello world" );

not gonna work, the compiler probably gonna tell you he can not find the test method.

When you declare something public, this attribute is open by default by the language,
when you use your own attributes, you don't have this kind of automated behaviour.

You have to explicitly tell that you want to use your namespace, and for that there is 2 way of doing it

you directly provide the path of the namespace

import test.ClassA;
import test.debug; //yes you have to import your namespace for it to be visible

var a:ClassA = new ClassA();
       a.debug::test( "hello world" ); //full path to the namespace containing your method
       //will trace "debug: hello world"

or even better, you explicitly tell the language you want to use the namespace

import test.ClassA;
import test.debug;

use namespace debug; //hey I want to use the namespace debug

var a:ClassA = new ClassA();
       a.test( "hello world" ); // and now you can directly access the method
       //will trace "debug: hello world"

and off course, you can switch between namespaces

import test.ClassA;
import test.release;

use namespace release; //hey I want to use the namespace release this time

var a:ClassA = new ClassA();
       a.test( "hello world" ); // and now you can directly access the method
       //will trace "release: hello world"

Pretty neat if you ask me.

why ?

You can really isolate your debug and release logic keeping the same method names,
it's like having two implementations in parallel. The disadvantage is that your class carry the two implementations at the same time,
so if you want one or the other and stay light in size better use conditional compilation instead.

But that's also my point, you do carry the two implementations, so you should be able to switch betweeen them.

selectable namespaces

A namespace is a constant, you can not change it's value.
But you can declare a variable of the type Namespace and fill its value at runtime ;).

Let's transform our example, so you can switch from one namespace to another.

package test
{
	public class ClassA
	{
                private var _ns:Namespace; //your variable
                
		public function ClassA( ns:Namespace = null )
		{
			if( !ns )
			{
				ns = debug;
			}
			
			_ns = ns; //you assign a value
		}
		
		public function test( msg:String ):void
		{
			_ns::test( msg ); //you reuse your var
		}
                
		debug function test( msg:String ):void
		{
			trace( "debug: " + msg );
		}
		
		release function test( msg:String ):void
		{
			trace( "release: " + msg );
		}
	}
}

wait, wait, wait ...

To really understand that you need more infos about namespaces.
Each namespace you define is considered unique by the system,
technically you can define the same variable name of a namespace in 2 different packages
but the system will see those namespaces as different.

package test
{
	public namespace debug;
}

and

package com.whatever
{
	public namespace debug;
}

same name but different identities!!

In the class above, we're passing the namespace as a reference.

In the comment above when I say "you reuse your var", you could see that as "you reuse the identity of the namespace".

in code you can do that with a property of your class

     this["test"]( "hello world" );

if test is a public method, or a visible method, the slot will be resolved from the string "test"
with a namespace value it work the same, but you can not resolve it from a string,
you have to pass the reference of the namespace (it is a constant).

so when we do that

		public function test( msg:String ):void
		{
			_ns::test( msg ); //you reuse your var
		}

_ns got resolved to the namespace we pass in the constructor, but there we can only pass the reference.

We define 3 methods test, one in the public namespace, one in the debug namespace and one in the release namespace.

The method in the public namespace is just a redirector who use the namespace notation.

Let's use it

import test.ClassA;
import test.debug;

var a:ClassA = new ClassA( debug ); //you pass your reference
       a.test( "hello world" );
       //will trace "debug: hello world"

So what happen really ?

you save the namespace debug in the _ns variable
first the public method test is called
and redirect to _ns::test
_ns::test is resolved to debug::test

Let's use it more

import test.ClassA;
import test.debug;
import test.release;

var a:ClassA = new ClassA( debug );
       a.test( "hello world" );
       //will trace "debug: hello world"

var b:ClassA = new ClassA( release );
       b.test( "hello world" );
       //will trace "release: hello world"

And you can do endless variations, for example, you could store the namespace in a public variable of the class and not even pass it to the constructor and so something like

import test.ClassA;
import test.debug;
import test.release;

var a:ClassA = new ClassA();
       a.context = debug;
       a.test( "hello world" );
       //will trace "debug: hello world"
       a.context = release;
       a.test( "hello world" );
       //will trace "release: hello world"

neat tricks with namespaces

Override the trace function

Oh yes you can. Let's see how :).

package test
{
	public class ClassA
	{
                public function ClassA()
		{
		}
		
		public function test( msg:String ):void
		{
			trace( msg );
		}
                
		protected function trace( msg:String ):void
		{
			public::trace( "[ " + msg + " ]" );
		}
	}
}

The function trace is defined in the public namespace at the anonymous package level,
it is publicly available everywhere in your code.

But here in the context of your class, if you define a trace function either in the protected or private namespace, your function will take the priority over the public trace.

At the end you still want to use the trace function, so you explicitly use the full path
including the public namespace.

No hack here, you just use your language to the fullest.

TODO (more to come)

  • customize MovieClip trick
  • hide parts of your public API
  • unit tests helper
  • advanced security class
Comment by AdamAdept@gmail.com, Apr 1, 2010

Can't wait to see the unit test helper!

Comment by v.srivat...@gmail.com, Feb 10, 2012

Hi,

When I use the same example, I am seeing an error message - "a file found in a source-path cannot have more than one externally visible definition". And the location of this error is unknown.

Can you please help me in handling this error?

Thanks

Comment by project member zwetan, Feb 13, 2012

when you receive this error "a file found in a source-path cannot have more than one externally visible definition" that means you try to define more than one public definition per file

you HAVE TO declare the definitions in separate files

eg.

  src
    |_ test
          |_ debug.as
          |_ release.as
          |_ ClassA.as

a single .as file can contain the definition of either

- a class - an interface - a variable - a constant - a function - a namespace

when you declare a namespace

package test
{
        public namespace debug;
}

you can consider "debug" as a constant, eg. it is unique and can not be redefined


Sign in to add a comment
Powered by Google Project Hosting