Better OOP Through Namespaces
Namespaces may make poor function pointers, but you’d be wise to not write them off so quickly. It turns out that they have an altogether different usage that can help you blend the speed advantages of public fields with the encapsulation and information hiding of getters and setters. Read on to learn more about this lovely compromise.
In addition to the Namespace
class, AS3 features a namespace
keyword, which is the subject of today’s article. So, let’s see how to use this keyword to declare a new namespace for a fictitious library called FooLib:
package foolib { public namespace foolibinternal; }
There are a few things to notice about the above:
- There is no
class
orinterface
definition in the file and there is no need for one. This is similar to declaring a package-level function. Here we have a package-level namespace. - The namespace does not need to be assigned a value.
- The file should be named
foolibinternal.as
and placed in thefoolib
directory.
Now that we have created a new namespace, let’s use it!
package foolib.movement { import foolib.foolibinternal; public class MoveDirections { foolibinternal static const MOVE_NONE:int = -1; foolibinternal static const MOVE_UP:int = 0; foolibinternal static const MOVE_DOWN:int = 1; foolibinternal static const MOVE_LEFT:int = 2; foolibinternal static const MOVE_RIGHT:int = 3; } }
Here we have made a class with some static constants, essentially an enumeration. The purpose of the class is to define the movement directions for some entity that is controlled by other classes in FooLib. Let’s see how choosing the foolibinternal
namespace is the superior choice in this case:
public
:MoveDirections
and its constants would become part of the API exposed to users of FooLib, needlessly adding to the complexity of the API by presenting a class that has no use outside of FooLib.private
: The enumeration would be completely inaccessible to any other class that wants to use it.protected
: Only subclasses could use the enumeration, which is a bizarre restriction.internal
: Only other classes in thefoolib
package could useMoveDirections
, limiting how classes can be logically split into appropriate packages, even sub-packages.
In contrast to all of the default access specifiers above, our custom namespace provides access to all of the classes in FooLib, regardless of whether or not they have subclassed or what package they are in. How do they get this access? Easy! Let’s see an example:
package foolib.entities { import foolib.foolibinternal; import foolib.movement.MoveDirections; public class Robot { private var moveDir:int; public function Robot() { use namespace foolibinternal; this.moveDir = MoveDirections.MOVE_NONE; } } }
There are just two easy steps here:
- Import the namespace just like you would import a class.
- Add a
use namespace X
in any function where you want access to the namespace.
If you don’t want to do the second step, you can use the namespace directly with this funky syntax:
package foolib.entities { import foolib.foolibinternal; public class Robot { private var moveDir:int; public function Robot() { this.moveDir = MoveDirections.foolibinternal::MOVE_NONE; } } }
However, there is a big caveat to this direct approach: it compiles to a dynamic access of the foolibinternal
namespace via a Namespace
class. Here’s how it looks in bytecode: (annotated by me)
getlex MoveDirections // get the MoveDirections class getlex foolibinternal // get the foolibinternal namespace coerce Namespace // force it to be a Namespace object getproperty MOVE_NONE // dynamically access the namespace's MOVE_NONE field
Compare this to the trivial bytecode when we use namspace foolibinternal
:
getlex MoveDirections // get the MoveDirections class getproperty foolibinternal::MOVE_NONE // statically access the class' MOVE_NONE field
So we’ve seen how to declare the namespace, declare fields with the namespace as an access specifier, and how to use those fields from other code. Given the huge performance penalty of using the Namespace
class, let’s look at a performance test to ensure that we’re not incurring any of that penalty when we use namespace
:
mynamespace.as
package { public namespace mynamespace; }
NamespacesTest.as
package { import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.utils.getTimer; public class NamespacesTest extends Sprite { mynamespace var myna:int; public var publ:int; private var priv:int; protected var prot:int; internal var inte:int; private var logger:TextField; private function log(msg:*): void { logger.appendText(msg+"\n"); } private var e:Enum; public function NamespacesTest() { logger = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); log("Namespace,Time"); testPublic(); testPrivate(); testProtected(); testInternal(); testMyNamespaceUse(); testMyNamespaceDirect(); } private function testPublic(): void { const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { publ; } var afterTime:int = getTimer(); log("public," + (afterTime-beforeTime)); } private function testPrivate(): void { const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { priv; } var afterTime:int = getTimer(); log("private," + (afterTime-beforeTime)); } private function testProtected(): void { const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { prot; } var afterTime:int = getTimer(); log("protected," + (afterTime-beforeTime)); } private function testInternal(): void { const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { inte; } var afterTime:int = getTimer(); log("internal," + (afterTime-beforeTime)); } private function testMyNamespaceUse(): void { use namespace mynamespace; const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { myna; } var afterTime:int = getTimer(); log("mynamespace (use)," + (afterTime-beforeTime)); } private function testMyNamespaceDirect(): void { const REPS:int = 100000000; var beforeTime:int = getTimer(); for (var i:int = 0; i < REPS; ++i) { mynamespace::myna; } var afterTime:int = getTimer(); log("mynamespace (direct)," + (afterTime-beforeTime)); } } }
I ran this performance test with the following environment:
- Flex SDK (MXMLC) 4.1.0.16076, compiling in release mode (no debugging or verbose stack traces)
- Release version of Flash Player 10.3.181.22
- 2.4 Ghz Intel Core i5
- Mac OS X 10.6.7
And got these results:
Namespace | Time |
---|---|
public | 218 |
private | 236 |
protected | 212 |
internal | 216 |
mynamespace (use) | 216 |
mynamespace (direct) | 14419 |
Here are the same results in chart form:
As you can see, there is basically no difference between any of the default access specifiers (public
, private
, protected
, internal
) or even the custom namespace when we access it via use namespace
. When we directly access the namespace and the compiler does the access dynamically via a Namespace
class behind the scenes, we incur a 66x performance penalty!
In conclusion, namespaces can be very useful. They allow you to essentially create a library-level access specifier with minimal extra requirements to access them and zero performance penalty as long as you don’t directly access them with the funky MoveDirections.foolibinternal::MOVE_NONE
syntax. Know of any other good uses for namespaces? Post a comment below!
#1 by Henke37 on June 13th, 2011 ·
Note: it is valid to place the use statement in the class instead of just one function.
#2 by jackson on June 13th, 2011 ·
Good tip!
#3 by bkogut on June 13th, 2011 ·
The example code does not compile, since you cannot declare a class using a custom namespace.
The compiler throws a #1116 error “A user-defined namespace attribute can only be used at the top level of a class definition.”
The correct MoveDirections should be :
package foolib.movement
{
. . import foolib.foolibinternal;
. . public class MoveDirections
. . {
. . . . foolibinternal static const MOVE_NONE:int = -1;
. . . . (…)
. . }
}
(and there’s a missing “import foolib.movement.MoveDirections;” in the “Robot” class, but it’s just nitpicking ;-) )
#4 by jackson on June 13th, 2011 ·
I’ve updated the article. Thanks for pointing these errors out; clearly I hadn’t compiled the example. :)
#5 by Jan on June 15th, 2011 ·
There is a another possible test:
package
{
import mypackage.mynamespace;
class MyClass ()
{
use namespace mynamespace; // with use outside function block.
}
}
#6 by jackson on June 15th, 2011 ·
That’s true. I’ve modified the performance test app and inspected the bytecode. There is no difference between
use namespace X
in the function anduse namespace X
in the class. Therefore you can feel free to use whichever you deem more appropriate.Thanks for the idea.
#7 by Cardin on June 22nd, 2011 ·
Oh my! I used namespace direct before in a performance critical portion of my code. Thanks for the heads up. =D
#8 by amene on December 27th, 2011 ·
thanks so much,i love it,thanks for sharing your information.