Saturday, February 16, 2013

Using namespaces

A real-world example of a namespace that is used to prevent name conflicts can be found in the flash.util.Proxy class that is part of the Flash Player API. The Proxy class, which is the replacement for the Object.__resolve property from ActionScript 2.0, allows you to intercept references to undefined properties or methods before an error occurs. All of the methods of the Proxy class reside in the flash_proxy namespace in order to prevent name conflicts.

To better understand how the flash_proxy namespace is used, you need to understand how to use the Proxy class. The functionality of the Proxy class is available only to classes that inherit from it. In other words, if you want to use the methods of the Proxy class on an object, the object’s class definition must extend the Proxy class. For example, if you want to intercept attempts to call an undefined method, you would extend the Proxy class and then override the callProperty() method of the Proxy class.

You may recall that implementing namespaces is usually a three-step process of defining, applying, and then referencing a namespace. Because you never explicitly call any of the Proxy class methods, however, the flash_proxy namespace is only defined and applied, but never referenced. The Flash Player API defines flash_proxy namespace and applies it in the Proxy class. Your code only needs to apply the flash_proxy namespace to classes that extend the Proxy class.

The flash_proxy namespace is defined in the flash.util package in a manner similar to the following:

package flash.util {
     public namespace flash_proxy;
}
The namespace is applied to the methods of the Proxy class as shown in the following excerpt from the Proxy class:

public class Proxy {
     flash_proxy function callProperty(name:*, ... rest) : *
     flash_proxy function deleteProperty(name:*) : Boolean
     ...
}
As the following code shows, you must first import both the Proxy class and the flash_proxy namespace. You must then declare your class such that it extends the Proxy class (you must also add the dynamic attribute if you are compiling in strict mode). When you override the callProperty() method, you must use the flash_proxy namespace.

package {
     import flash.util.trace;
     import flash.util.Proxy;
     import flash.util.flash_proxy;
     dynamic class MyProxy extends Proxy {
          flash_proxy override function callProperty(name:*, ...rest):* {
                trace("method call intercepted: " + name);
                }
         }
}
If you create an instance of the MyProxy class and call an undefined method, such as the testing() method called in the following example, your Proxy object intercepts the method call and executes the statements inside the overridden callProperty() method (in this case a simple trace() statement).

var mySample:MyProxy = new MyProxy();
mySample.testing(); // Output: method call intercepted: testing
There are two advantages to having the methods of the Proxy class inside the flash_proxy namespace. First, having a separate namespace reduces clutter in the public interface of any class that extends Proxy. There are about a dozen methods in the Proxy class that you can override, all of which are not designed to be called directly. Placing all of them in the public namespace could be confusing. Second, use of the flash_proxy namespace avoids name conflicts in case your Proxy subclass contains instance methods with names that match any of the Proxy class methods. For example, you may want to name one of your own methods callProperty(). The following code is acceptable because your version of the callProperty() method is in a different namespace:

dynamic class MyProxy extends Proxy {
     public function callProperty() {}
     flash_proxy override function callProperty(name:*, ...rest):* {
          trace("method call intercepted: " + name);
     }
}
Namespaces can also be helpful when you want to provide access to methods or properties in a way that cannot be accomplished with the four access control specifiers (public, private, internal, and protected). For example, you may have a few utility methods that are spread out across several packages. You want these methods available to all of your packages, but you don’t want the methods to be public. To accomplish this, you can create a new namespace and use it as your own special access control specifier.

In the following example, a user-defined namespace is used to group together two functions that reside in different packages. By grouping them into the same namespace, you can make both functions visible to a class or package through a single use namespace statement.

This example uses four files to demonstrate this technique. The first file, myInternal.as, is used to define the myInternal namespace. Because the file is in a package named example, you must place the file into a folder named example. The namespace is marked as public so that it can be imported into other packages.

// myInternal.as in folder example
package example {
     public namespace myInternal = "http://www.adobe.com/2006/actionscript/examples";
}
The second and third files, Utility.as and Helper.as, define the classes that contain methods that should be available to other packages. The Utility class is in the example.alpha package, which means that the file should be placed inside a folder named alpha that is a subfolder of the example folder. The Helper class is in the example.beta package, which means that the file should be placed inside a folder named beta that is also a subfolder of the example folder.Both of these packages, example.alpha and example.beta, must import the namespace before using it.

// Utility.as in the example\alpha folder
package example.alpha {
     import example.myInternal;
     use namespace 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;
     use namespace myInternal;
public class Helper {
     private static var _timeStamp:Date;
     public static function someTask() {
          _timeStamp = new Date();
}
 myInternal static function get lastCalled ():Date {
      return _timeStamp;
       }
    }
}


The fourth file, NamespaceUseCase.as, is the main application class, and should be a sibling to the example folder. The NamespaceUseCase class will also import the myInternal namespace and use it to call the two static methods that reside in the other packages. The example uses static methods only to simplify the code. Both static and instance methods can be placed into the myInternal namespace.

// 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
     import flash.util.trace;
     use namespace myInternal;
          public class NamespaceUseCase extends MovieClip {
                public function NamespaceUseCase() {
                Utility.someTask();
                Utility.someTask();
                trace(Utility.taskCounter); // Output: 2
                Helper.someTask();
                trace(Helper.lastCalled); // Output: [time someTask() last called]
                }
         }
}
Programming Actionscript 3. Powered by Blogger.