ActionScript 3: The Facts Behind the Basics - Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Chapter 2. ActionScript 3: The Facts Behind the Basics

As you know, a procedure can be carried out several ways. “Hello World,” for example, is often the very first application implemented when you’re learning a language. If you were to write down the many unique ways to output “Hello World,” I’ll bet you could devise at least 10. The more you learn the API of a language, the more options available to you as a developer.

Not every object-oriented language is written similarly. Each language has its own set of nuances that developers come to love or dread. To best implement your object-oriented code, you must become familiar with such aspects of the ActionScript 3 language.

This chapter explores specifics of the ActionScript language. It will benefit your object-oriented implementations and further your understanding of the language.

ActionScript 3

ActionScript is a total rewrite of its lingual predecessors. Originally, ActionScript followed the Ecma standards and was modelled around the prototype as the means to develop classes. To add to the object hierarchy, you used the fundamental prototype object to model objects with the properties of another object. In ActionScript 2.0, the class directive was added, but the use of the word class was nothing but a superficial way to work with objects. It didn’t change the fact that behind the scenes, the prototype continued to link the classes together.

It wasn’t until ActionScript 3, which finally moved toward a class-driven, object-oriented language, that actual change took place behind the scenes of compilation. These tweaks brought both good and bad and included the following: performance, garbage collection, the event model, strong typing, the display model, and method closures.

The Traits Object

New to the ActionScript language, the traits object was added to provide true class inheritance. The inclusion of a traits object greatly reduces the delay caused by property lookup. In previous ActionScript languages, object properties were shared among cloned objects by what was known as the prototype chain. If the property targeted on an instantiated object couldn’t be found, the expected property is searched for within the next object up the chain, and the search continued until the top-level object was found.

The traits object vastly enhances property lookup by eliminating the need for the prototype chain. Every class, when compiled, possesses a number of objects, one of which is the new traits object. The traits object is supplied with all the properties that are inherited. As long as the class is not marked as dynamic, performance is significantly improved.

Although the traits object is new to ActionScript 3, it doesn’t fully replace the prototype object. To remain compatible with the Ecma specification, the prototype object remains, but the ActionScript 3 preferred manner of inheritance is to use the traits object and fixed inheritance. Only properties declared within a class can be passed, versus dynamic assignments of properties and methods at runtime. If you’ve ever opened a top-level class, this is the reason it contained function declarations.

Although the traits object remains behind the scenes and isn’t accessible by code, it’s the model for each object among many of the methods you’ll see in this chapter.

Garbage Collection

Each object created in an object-oriented programming (OOP) language requires a particular allocation of system memory. Each object’s allocation of memory varies, but the more objects created, the more memory consumed, and the fewer resources remain available. When objects are no longer used by the system, they’re gathered and destroyed in order to reclaim the memory they consumed.

Compartmentalization makes a system more flexible and modular but increases the number of objects used in an application. Using design patterns to achieve a flexible and loosely coupled architecture enables code reuse and polymorphism. However, the collaborations among objects that allow for such flexibility require attention to memory management. This isn’t a drawback to the patterns themselves, but it’s a reality that OOP developers must consider as Garbage Collection requires the Developer to be proactive.

Memory Management

Knowing how to eliminate weeds is great, but understanding how to prevent their growth is even better. It’s no surprise that the majority of Flash developers aren’t computer science majors. It’s also no surprise that being a Flash developer has evolved from a hobby to a profession. Therefore, you can understand why developers often fight memory management as a result of poor performance.

The transition to ActionScript 3 has introduced developers to memory management, which for many is an incredibly new concept. Those who migrated from previous versions of ActionScript have never concerned themselves with memory, and those who have developed solely with ActionScript 3 have focused on memory almost as little as those who transitioned from an earlier release.

The most difficult aspect of memory management is understanding the memory used within the application. Most of the ActionScript literature focuses on removing events as the end of the memory consumption issue.

The tool to understanding memory consumption is the flash.sampler package, which also isn’t well known among developers (see Figure 2-1).

image

Figure 2-1. The flash.sampler package and its contents

The sampler package was originally supplied with Flex and was uses by a profiler added to the compiler. The package gives you greater insight into the internal workings of the role each object plays and the resulting impact on memory resources. Even if you don’t have a special editor such as FDT or Flash Builder, you can fully use the contents of this package; the only requirement is that you run the compiled code in the Flash Player Debugger version 9.0.115.0 or later.

It goes without saying that the more specialized the object hierarchy is, the more memory it probably requires. Without knowing how memory use varies among objects, you can’t choose the objects that best suit the requirements of the chosen behavior. The static methodgetSize(obj:Object):Number in the sampler package enables you to view the amount of memory resources used by an application object.

The object passed as a parameter of the getSize method is analyzed. The return value is the number of bytes in memory the object uses. Here’s an example:

trace( getSize( new Object() )); //results in 40 Bytes;

As you see, the instantiated Object is passed into the getSize method, and its value in memory is returned (40 bytes in this case).

Figure 2-2 shows that each object has a specific value that it imposes on memory resources. A reference alone, either Complex or Primitive—excluding Number—reserves 4 bytes of memory. Number, because it’s a double float precision, requires twice that amount (8 bytes).String is a slightly different matter. String values are calculated based on the characters used; the memory required varies depending on whether the string is static or dynamic at the time of reference creation, and whether it’s a single character.

image

Figure 2-2. The results of a few trials of the getSize method

Using getSize, you can compile a class and calculate the number of bytes your class adds as overhead in an application. The code in Listing 2-1 defines class Circle, which extends the built-in ActionScript 3 object Shape. The class has the properties such as_color,_radius, and_object.

Listing 2-1. The Circle class extends Shape and has three properties: color, radius, and object

package
{
import flash.display.Shape;

public class Circle extends Shape
{
private var _color : uint;
private var _radius : Number;
private var _object : Object;

public function Circle( radius : Number )
{
_radius = radius;
_object = new Object();
}

public function get color() : uint
{
return color;
}

public function set color( color : uint ) : void
{
_color = color;
}
}
}

Before you instantiate the Circle class, let’s predict how much memory will be used by an instance of a Circle object. You can refer to Figure 2-2 for assistance.

The Circle class inherits its properties from Shape, which you know uses 224 bytes of memory. It has two primitive references: a Number (8 bytes) and an unsigned integer (4 bytes). It also has an Object reference, which you know requires 4 bytes. Now, if you add those values, you can predict that an instance of Circle requires approximately 240 bytes.

If you were wondering why the instantiation of Object within the constructor isn’t included in the equation, the answer is simple. Although you know that an instance of Object requires 40 bytes, only primitives retain a value; complex references of objects are merely pointers (you learn more about pointers in the section “Mark and Sweep”). These pointers refer to the location in memory where these objects exist. 40 bytes are added to your application as soon as you instantiate the Circle object, but those 40 extra bytes are calculated as part of the total memory consumed, not in the Circle instance.

trace( getSize( new Circle( 0 ) ) ); // 240 Bytes

Note that the memory required is 240 bytes. Due to the many bug fixes implemented from one player to the next the memory consumption varies. The values used here are from the Flash player 10.1.102 build.

Let’s look at another example. This time, we’ll use MovieClip as the superclass. Let’s name this class MovieClipExtension and, for demonstration purposes, supply absolutely nothing beyond a constructor:

package
{
import flash.display.MovieClip;

public class MovieClipExtension extends MovieClip
{
public function MovieClipExtension()
{
}
}
}

If you estimate the file size, you assume it’s that of an instantiated MovieClip, or 428 bytes. But here’s the call to getSize():

trace( getSize( new MovieClipExtension ( ) ) ); // 412 bytes

The application states that MovieClipExtension consumes 412 bytes of memory resources, which is not what you expected. However, recall that MovieClip is a dynamic class: therefore it supplies memory for a hashtable, where it stores dynamically added properties at runtime.MovieClipExtension, on the other hand, wasn’t declared as a dynamic class and is considered a sealed class by default. The hashtable that resides in the instance of a dynamically defined MovieClip isn’t added to the traits object of MovieClipExtension, and therefore you save a few bytes. If you were to declare your new class as dynamic, getSize would reveal MovieClipExtension’s memory consumption to be equal to that of an instantiated MovieClip.

The impact of 412 bytes on a system may not appear to be much; in fact, it may appear to be laughable. But in any application, bytes can add up quickly. In a system like ActionScript 3, where garbage collection can’t be forced and is activated only when too much memory has been consumed, preserving memory resources is a vital. Choosing the appropriate objects is a must in any object-oriented language, and you must reflect this in the classes for your patterns.

Mark and Sweep

To reduce the overhead of running a tedious algorithm, which can stutter the player’s performance, the garbage collector (GC) is triggered only at a specific point. The GC uses a mark and sweep approach, where as long as zero pointers target a location in memory, that memory is considered eligible to be emptied. As you know, in ActionScript, a primitive reference is an actual copy; therefore it isn’t as much of a threat in an object’s persistence as a complex reference. Complex references are physical pointers to memory locations, as a means of maintaining memory. Although pointers aid in reducing memory duplication, they also prevent the GC from dumping its target.

Let’s look at an example. In Figure 2-3, although it appears that a variable possesses properties and methods declared by the new instance of Object, the properties aren’t copied onto it. Rather, a location of blocks in memory is endowed with all that your template has to pass on, and then a direct connection to its location, like a bridge, is adhered to a reference.

image

Figure 2-3. Instantiation and memory location referenced

Only by nullifying this bridge can you make the GC view the memory as no longer being referenced within the application (see Figure 2-4). Doing so enables the GC to dump the contents of data blocks that are no longer being used, thus freeing up memory. But while the memory address is in use, other variables can point to the same location in memory, thus preventing the release of memory from your program even when obj’s bridge is nullified.

image

Figure 2-4. Location to memory severed

In order to efficiently free up memory, you must set to null all references to any memory location, as shown in Figure 2-4. Doing so marks the location as eligible to be freed. All references that are marked are added to a zero count stack, where it’s determined whether they’re available to be emptied.

Figure 2-5 demonstrates that while the reference ‘obj’ may be set to null, secondaryObj has not been, and therefore the memory consumed from the Objects instantiation cannot yet be reclaimed.

image

Figure 2-5. Instantiation and memory location referenced via secondaryObj;

Design patterns, whether creational, behavioral, or structural, pass references for delegation and modification. Some examples are the Observer pattern (discussed in Chapter 7) and the Command pattern (also discussed in Chapter 7), just to name two. The relationships among objects may prevent memory from being released, so you need to ensure this release when you no longer require the objects’ services.

Implementing a Disposable Pattern

Hooray, your first pattern! The Disposable pattern, as it’s appropriately named, is one of many behavioral patterns. The intent of this pattern is to separate the logic required among objects from the modeled behavior defined in the abstract class during the removal of composed references. As you can see in Figure 2-6, the collaborators in the pattern are as follows:

1. Disposable interface

2. Abstract class

3. Concrete class

image

Figure 2-6. Class diagram of the Disposable pattern

Without specific lingual pattern interpretation, you can’t merge such a pattern into an ActionScript system, due to the language’s preexisting conditions. The reasons are as follows.

First, there are no proper directives that can absolutely enforce an abstract class, as of the current release of ActionScript 3. You can pretend a class is abstract, but there is no foolproof way to ensure that this class will never be used. As the definition of an abstract class specifies, it’s a class that will and can never be instantiated.

The closest you can get to an abstract class is to create a class that throws an error in the constructor, preventing the compiler from continuing without this error being corrected (see Listing 2-2).

Listing 2-2. Faux abstract class in ActionScript 3, which results in an error if you try to instantiate it

package
{
import flash.errors.IllegalOperationError;
public class AbstractClass
{
public function AbstractClass()
{
throw new IllegalOperationError( "This class is intended as an abstractimage
class and mustn't be instantiated" );
}
}
}

This faux manner of devising an abstract class does the intended job of enforcing that the class can’t be instantiated. However, it lacks proper ability to enforce its subclasses to override all abstract methods it contains. I’ve seen some clever means by which this has been made possible, but enforcement is only available at runtime, which can slow development.

Image Note Feel free to explore “Runtime Enforcement of Abstract Classes in AS3” by Josh Tynjala at https://web.archive.org/web/20130709134147/http://joshblog.net/2007/08/19/enforcing-abstract-classes-at-runtime-in-actionscript-3/.

The next best option is to inject your method directly into your top-level class, thus trickling it into any subclass defined by a developer. Unfortunately, this is also impossible while adhering to fixed inheritance. In an effort to obtain optimal performance and use as few memory resources as possible, properties and methods are added to the traits object using fixed inheritance, as discussed earlier. Fixed inheritance also prevents you from adding methods and properties at runtime via the prototype object. To inject a dispose method into the language’s classes, you need to physically modify the classes that came with your OOP language. This becomes an issue in itself, because each developer must have the same modified language classes, which can cause a lot of confusion with future releases and legacy code. Plus, if new developers are hired, they too need to modify their classes.

Because you can’t rely on inheritance as the sole means to establish your disposable method, you’re left with only one viable solution to make this pattern available: implement the interface into every custom class defined. This is an efficient approach that all developers can use. It’s the ideal implementation for the Disposable pattern in the ActionScript 3 language.

Not every object in ActionScript requires a null value to its reference, although it’s better to overdo it than not. Primitive data types do allocate memory within an application, but they do so only for the lifespan of the object in which they’re declared. Complex types, on the other hand, require all pointers to be severed. The implementation of the destroy method is specific to the class and the references that it contains.

To properly remove all complex objects, it helps to be able to see all references within a class. This allows you to directly target the references and to set them to null in the disposal method. Unfortunately, when you use nested library clips, the instance names are often added to the classes at compile time. This is known as stage instance declaration, and each project, by default, is an automatic occurrence.

For example, using the flash.sampler package, let’s demonstrate the compiled complex reference that is inserted into an object using the static method getMemberNames(). This method accepts two parameters: the object, from which it retrieves all QName members; and whether to include any instance names that may be available to the object.

QName is an ActionScript 3 class; it’s short for qualified name. Chapter 1 discussed how the compiler can locate and refer to a particular definition with a URI along with the definition’s name. All definitions in the language are referenced absolutely behind the scenes as qualified names, which eliminates any ambiguity about which definition is being referred to. This is referred to as being fully qualified.

Therefore, when an instance member is found and returned via getMemberNames, it’s returned as a qualified name. Appropriately, QName provides two properties that a qualified name uses: localName and uri. localName represents the member name, and uri is the namespace in which localName remains unique. Here’s an example making use of our TestClip from Figure 2-7:

var tc:MovieClip = new TestClip()
for each ( var members:QName in getMemberNames( tc , true ) )
{
trace( members.localName ); //inner_mc, currentScene, currentFrameLabel, etc..
}

image

Figure 2-7. A nested clip whose instance name is that of inner_mc in TestClip

As you can see, inner_mc is added as an object reference in the TestClip object. What is slightly misleading is that you may think you’re calling the clip through the instance name, but the compiler inserts an identifier to match that of your declared instance name.

Image Tip As a best practice, you should always deselect Automatically Declare Stage Instances, to enforce your class physically declaring any necessary instances with which it associates.

To deselect this option in Flash CS5, choose File image Publish Settings image Flash image Settings, and locate the Stage property as shown in Figure 2-8.

image

Figure 2-8. Deselect the Automatically Declare Stage Instances option

Unfortunately, this option defaults to being selected on a per .fla basis.

If you remove ActionScript’s ability to supply instances automatically to your code, you’re required to manually declare any and all instances as properties among the appropriate classes.

Manually Declared Stage instances

With your handy dandy object-oriented skills, you know that it’s always wise to separate the implementation from its structure, allowing for flexibility. Having disabled automatic stage instance declarations, you have to declare the reference yourself; otherwise, when you compile your code, aReferenceError occurs:

ReferenceError: Error #1056: Cannot create property inner_mc on TestClip.
at flash.display::Sprite/constructChildren()
at flash.display::Sprite()
at flash.display::MovieClip()
at TestClip()
at DocumentClass()

To add a variable representing the nested clip, open the TestClip class and add private var inner_mc. But you find that again, at compile time, you’re confronted with a ReferenceError. Unless you specifically declare inner_mc as a public variable, you continue to face the same ReferenceError. Why? This has to do with the order in which things are compiled and the hashtable used by MovieClips that enabled the clips to be added. Because MovieClips let you add dynamic properties, its references must remain public. By extending theMovieClip class to allow TestClip to be defined, the hashtable is removed unless you specify dynamic behavior for TestClip as well. This allows the nested clip to be accepted after your classes are compiled. If you were to define your clip as dynamic, you could get away with defining inner_mc as private. Again you’ve broken the concept of encapsulation, because you’re allowing dynamic behavior on the class. This is exactly what would occur if you declared TestClip as a sealed class and used it as the base class of ContainerClip.

image

Figure 2-9. Clip using TestClip as its base class

Now, when you compile the clip, because you’ve defined private var inner_mc in the class TestClip, the compiler no longer throws a reference error. The reason is apparent when you use the describeType method from the flash.utils package. This method reveals the details of the parameterized object instance, in the form of XML:

trace( describeType( new SpecificallyGivenName() ) )
// <type name=" SpecificallyGivenName " base="TestClip" isDynamic="true"image
isFinal="false" isStatic="false"> ...

When the Flash compiler can’t find a class labeled SpecificallyGivenName, it supplies one at the time of compilation; and to fulfill its role, SpecificallyGivenName is defined as being dynamic. This leaves you with the solution of defining your references as being public, so you can continue to seal your class to further maintain data hiding.

Application Domain

The application domain, not to be confused with problem domain, represents the memory location of an application’s given definitions. When a .swf file is published, the compiler bundles all application definitions into that of an application domain. If a .swf is loaded into another .swf, the definitions of both .swfs remain partitioned from one another. This ensures that the definitions of one application don’t interfere with the naming conventions of possibly same named definitions between the two .swf files.

You instantiate an ApplicationDomain by importing the flash.system.ApplicationDomain class. The ApplicationDomain class, when instantiated, accepts as an optional parameter a reference to a preexisting applicationDomain. Specifying a preexistingapplicationDomain lets the devised partition use definitions from the passed-in applicationDomain (appDom for short).

Two important properties of the ApplicationDomain class are currentDomain and parentDomain. currentDomain is a static property that points to the applicationDomain, which holds the code currently being executed. parentDomain is a pointer to the parent’sApplicationDomain, providing one exists.

By default, when a .swf file is published, its applicationDomain doesn’t have a parentDomain, and all definitions are considered to be stored in what is referred to as a systemDomain. This is where built-in definitions of the ActionScript 3 language are contained (MovieClip, Sprite, Loader, and so on). The packaged definitions are partitioned onto that of the systemDomain, allowing all user-defined code to refer to the built-in definitions. Only when a .swf file is loaded into another .swf is a parentDomain possibly available. Possibly, because a parentDomain is available only when an instantiation of an ApplicationDomain includes a reference to an existing applicationDomain, thus creating a hierarchy among the appDoms.

When you load one .swf file into another, you can modify the partitioning of the loading .swf’s definitions by specifying one of the following four application domain settings:

· Child of the loader’s ApplicationDomain: The default setting when loading a .swf file, applied using new ApplicationDomain(ApplicationDomain.currentDomain). This line of code creates the new application domain on the loading .swf, but with a relationship to the application domain of the parent container. Attaching the ApplicationDomain on the parent’s appDom allows the loaded .swf file to use the parent’s definitions by referring to them as if they were located in the loaded .swf file’sApplicationDomain.currentDomain.

· Loader’s ApplicationDomain: Specified as ApplicationDomain.currentDomain. No partition is created, allowing all definitions of the child to be loaded into the loader’s appDom. This is, of course, with the exception of duplicate definitions, which are disregarded.

· Child of the system ApplicationDomain: Specified as new ApplicationDomain( null ). Creates a partition among the child and parent definitions. This ensures that definitions of similar names don’t interfere with one another. It also ensures that the definitions of the two .swfs aren’t visible to one another, thus isolating definitions between the two.

· Child of a specified ApplicationDomain: Specified as ApplicationDomain( 'application_domain_here' ). When you specify the relationship between a loading .swf file’s definitions and another, you can partition the ApplicationDomainamong the child’s appDom with the visibility of another appDom’s definitions. You can do so via the parentDomain property or through a reference to an appDom.

You can use the specification among ApplicationDomains only when loading .swf files published for the ActionScript 3 language. To specify the ApplicationDomain settings, a property on the LoaderContext object must reflect such changes, as you’ll see next.

The LoaderContext

LoaderContext is an object that, when instantiated, can be passed into a Loader object, allowing for the modification of additional options. One such option is the applicationDomain property, which you can set by supplying one of the four values listed in the previous section to the LoaderContext.applicationDomain, as shown in Listing 2-3.

Listing 2-3. Specifies the appDom of the loading definitions to be included within the applicationDomain to which the loader’s definitions exist

var loader:Loader= new Loader();
var urlRequest:URLRequest = new URLRequest('externalSWF.swf');
var loadContext:LoaderContext= new LoaderContext();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loadContext.applicationDomain = ApplicationDomain.currentDomain;

loader.load(urlRequest, loadContext);

Currently you’ve set the ApplicationDomain of the loading .swf file as a child of the loader’s, of the 'externalSWF.swf's; applicationDomain.

Specifying the applicationDomain among definitions enables both the compiler and you as a developer to tap into the scope of existing definitions. Specifying the applicationDomain among loaded definitions allows for definition reuse, similar to the use of runtime shared libraries.

By understanding how definitions are compiled into the appDoms, you can further your understanding of how you work with objects and their instantiations, via the use of the new operator, as discussed in the next section.

The Class Object

All definitions in ActionScript 3 are instances of the built-in Class object. Although the Class object is of little use to a developer, its instances are a different matter. You use these all the time when you use the new operator. Each Class object can be referenced by the name it was given when you specified its external definition. As you saw earlier in Figure 2-3, any reference that remains within scope can be obtained.

Fortunately, the scope of your application, and your definitions’ longevity, can coincide with the applicationDomain of the particular .swf file. If you have a reference to the current appDom and want to retrieve a particular Class object with which to work, you can do so using the getDefinition and getDefinitionByName methods.

Suppose the externalSWF.swf file from Listing 2-3 was defined by an attached class DocumentClass, shown in Listing 2-4.

Listing 2-4. Base class of externalSWF

package
{
import flash.display.Sprite;

public class DocumentClass extends Sprite
{
public function DocumentClass()
{
// constructor code
trace( 'DocumentClass instantiated' );
}
}
}

The base class, DocumentClass, is converted into a Class object and appropriately partitioned in one of the four manners the applicationDomain allows.

getDefinition( )

The getDefinition method retrieves a Class object from an existing applicationDomain explicit to the definition name parameterized within. This is reflected in the method signature: getDefinition(memberName:String):Class.

To demonstrate how you can use getDefinition to retrieve a Class object, specify DocumentClass as the memberName you wish to retrieve from the applicationDomain of externalSwf.swf from within the currentDomain of the parent .swf file (see Listing 2-5).

Listing 2-5. Retrieving the DocumentClass Class object from the currently executing appDom via getDefinition

var loader:Loader = new Loader();
var urlRequest:URLRequest = new URLRequest('externalSWF.swf');
var loadContext:LoaderContext = new LoaderContext();
loadContext.applicationDomain = ApplicationDomain.currentDomain;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onComplete);
loader.load(urlRequest,loadContext);

function onComplete(e:Event) :void
{
var returnedObject:Class = e.target.applicationDomain.getDefinition('DocumentClass');
trace(returnedObject); //[class DocumentClass]
}

As demonstrated in the Listing 2-5, you can retrieve the DocumentClass Class object from the appDom of the currentDomain after externalSWF.swf has been loaded. When you obtain the desired Class object, you can do with it as you see fit. You can pass the reference, retain the reference, or even instantiate it via the new operator.

The returned Class object of the getDefinition isn’t limited to class definitions. The returned object can be a class, a namespace, or even an interface; all are subclasses of the Class object.

getDefinitionByName( )

Much like getDefinition, getDefinitionByName also retrieves a public Class object from an ApplicationDomain explicit to the definition name parameterized within. The difference between the two is that getDefinitionByName is a global function and can only return a Class. In other words, don’t expect to retrieve namespaces or interfaces via getDefinitionByName. Listing 2-6 shows an example.

Listing 2-6. Retrieving the DocumentClass class object from the currently executing appDom via getDefinitionByName

var loader:Loader = new Loader();
var urlRequest:URLRequest = new URLRequest('externalSWF.swf');
var loadContext:LoaderContext = new LoaderContext();
loadContext.applicationDomain = ApplicationDomain.currentDomain;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onComplete);
loader.load(urlRequest,loadContext);

function onComplete(e:Event):void
{
var returnedObject:Class = Class(getDefinitionByName('DocumentClass'));
trace(returnedObject); //[class DocumentClass]
}

As you can see, getDefinitionByName doesn’t need to succeed the reference of an applicationDomain, because it always points to the currently executing appDom. Another noteworthy difference is that getDefinitionByName returns a Class object, but the method’s signature specifies a return type of Object. To provide strong-typing to the reference, knowing the object is a Class object, you can cast the return value to be Class:

var returnedObject:Class = Class(getDefinitionByName('DocumentClass'));

The next section takes a look at what it is to Strong Type a reference.

Strong Typing

Data typing is nothing new to the ActionScript language, but the performance gained by supplying data types is. Strong typing facilitates proper use of objects in a system by associating, or binding, the reference to an object’s intended interface. Associating a variable with a data type optimizes the bytecode and improves player performance significantly.

Although performance should be reason enough to use typing data, it also provides many benefits in terms of productivity by reducing errors.

Because ActionScript is a dynamically typed language, it provides runtime type checking. Although this makes the language incredibly versatile, it leads to slower debugging, because errors are revealed only when your code is running. Fortunately, the Flash IDE (when in strict mode) offers compile-time checking as well.

Runtime Type Checking

Dynamic languages let an application modify its behavior at runtime. When a program can constantly adapt, you can only verify mismatch errors and type coercions with runtime type checking.

Runtime type checking, as the name suggests, ensures there no improper mismatches take place while the application is running. To accomplish this, the interpreter must determine and imply data types as they’re being used. Thus, although runtime type checking allows the language to remain fluid, it can slow the debugging process.

New to ActionScript 3 is the addition of compile-time type checking, which lets you predict and prevent any possible mismatch or type-coercion errors that may occur while your code is running.

Compile-Time Type Checking

Typing a reference preserves the contract among object messaging within the system. It there’s any incorrect use of the object through an attempt to call a method or property, you’re made aware of it either at the moment of compilation or even while authoring, via a type-mismatch error. The editor may determine the means by which you’re informed. Compile-time type checking also ensures the proper passing of references and assists you if any improper mismatches occur during development.

Compile-time checking is optional, and its use does not prevent runtime type checking from occurring. Using compile-time type checking doesn’t ensure that all runtime errors are eliminated.

By default, the Flash IDE settings enable compile-time checking. Deselecting the Strict-Mode option in settings defers all type checking to runtime checking.

Although strict mode provides type checking, data types among variables and/or expressions must be typed within your code.

Restricting a Dynamic Language

The benefit of typing variables, parameters, and return types is the optimization of ActionScript bytecode (ABC), which reduces the interpreter’s need to imply data types at runtime. It also gives you immediate feedback from the editor in an effort to prevent type errors early on.

Practicing optimal implementation of design patterns in ActionScript 3 requires you to choose the most appropriate types to bind to the expressions and variables among each pattern’s collaborative objects. Using types that are overly specific reduces the pattern’s reusability, leading to tightly coupling. However, falling back on an overly generalized type requires careful collaboration and attention to your system and its parts. The beauty of many patterns is their ability to assist with generalizations among objects that you may not otherwise have foreseen.

Unfortunately, subclasses can’t redefine types among variables or expressions defined by their superclass. This often reduces code reuse because you must write more to satisfy such needs. If code reuse appears to outweigh the optimization of typing, ActionScript offers a solution.

Although I’m not advocating the use of weakly typed data or the lack of typing, the compiler isn’t as flexible the interpreter, known as AVM2. Because ActionScript is a dynamic language, runtime reflection is possible, which can make your applications more flexible.

ActionScript 3 lets you bypass compile-time checking by using the untyped property or wildcard annotation represented by the multiply symbol *. Assignment to an untyped property doesn’t cause compile-time errors because the reference is recognized as holding any value. These assignments are, on the other hand, type-checked and properly cast at runtime.

Casting

When you pass references among objects to that of a generalized type, you often have to cast an object back into its actual data type. There are two ways to cast an object in ActionScript 3: the cast operation, which wraps an expression such as Type( expression ), and the as operator( expression as DataType ). Both approaches have distinct advantages over the other, such as readability, conversion precedence, performance, and casting failures.

Typically, in ActionScript 3, you use the as operator to properly cast an object into another data type. Why? Because, unfortunately, too many developers mimic behavior presented to they learned the language. This is due to three reasons.

First, not every cast works properly when used as a wrapped expression. The following code compares the readability of type casting using the two acceptable approaches:

obj as MovieClip versus MovieClip(obj)

To many developers, it’s clearer that obj as MovieClip is type casted. MovieClip(obj) may be mistakenly read as a new Instantiation.

Second, the attempted casting of an array to an object and back to an array demonstrates a casting faux pas when using the wrapped cast of an expression:

var ar:Array = [ 'bob' , 'tom' , 'mike' ];
var obj:Object = ar;
var arTyped:Array = Array( obj );
trace( arTyped.length ); // 1
trace( arTyped[ 0 ] ); // 'bob','tom','mike'
trace( arTyped[ 0 ][ 0 ] ); // 'bob'

arTyped = obj as Array;
trace( arTyped.length ); // 3

Unfortunately, not all casting operations work as you may expect. In this example, attempts to upcast the Array to an Object and downcast it back to an Array aren’t particularly successful. You expect to end with an array that possesses an index of 3. The result is correct when you use the as operator but not otherwise. Instead, your object is downcast to an array and then wrapped in the first index of a new array.

Finally, the following attempt to cast the instance of an object into that of a MovieClip demonstrates the cast failure between both cast manners:

var object:Object = {}
trace( MovieClip( object ) ) // TypeError: Error #1034: Type Coercionimage
failed: cannot convert Object@30f68f71 to flash.display.MovieClip.

trace( object as MovieClip ); // null

When a cast is successful, the original object is returned for continued use without interruption. When a cast is unsuccessful, the object isn’t returned, and any reference to the expected return is set to null.

As you can see, you can use as to successfully cast an object without any unexpected side effects. Unfortunately, as you can also see, using casting via the as operator doesn’t offer a type-cast error at runtime.

The truth is that the as operator is useful, but it doesn’t offer much in the way of development beyond readability. Although it successfully casts an Array, it offers no assistance in the way of debugging your system. This is why you should avoid the as operator for type casting if possible and instead use a cast operation.

Configuration Constants

Every tool has a particular feature that makes it useful. A screwdriver is a simple tool, and yet it’s the most commonly used. As often as you use it, you keep it tucked away, out of sight. For whatever reason, a screwdriver isn’t a conversation piece. This seems to be the case with one of my favorite features of the Flash IDE: the configuration (config for short) constants.

Throughout the build process of any rich Internet application (RIA), it’s uncommon to succeed without the assistance of debugging. During this process, you can end up with numerous unnecessary lines of code that wind up getting compiled into your application. These lines are not unlike those of traces, but the Flash compiler offers a means to remove such declarations from your code. It also lets you eliminate specific statements if they’re based on the condition of a config constant. The Flash compiler includes a few perks; config constants are one of them. Consider the following lines of code:

//handle intro if the swf is not loaded into a container
if ( this.parent != null ) {
force_intro()
}

Often, when working in a team environment, developers toggle code on and off when attempting to test and debug code that is meant to work with a framework after it’s loaded into another .swf file. Typically, in such web site development, a loaded asset doesn’t perform its intro until told to do so by the container. In order to test a file properly, you must either test it within the container or remove the code that prevents the .swf file from running its intro.

The previous code demonstrates a conditional statement that runs a particular operation if the parent of the .swf file doesn’t exist. Suppose this conditional statement runs in the constructor of the document class. Because the .swf file runs outside of the container, there is no parent; therefore you force the intro to occur on your application. Throughout the duration of the build, you add a slew of conditionals to ensure that things work according to your needs. The more you allow such conditionals to build up, the less likely you are to remove them later, and the more memory you use.

This is where the beauty of the config constants comes into play. If you construct your code around an intended config constant, you can easily remove those lines from the compilation of the .swf file, therefore reducing file size and enhancing performance.

To use config constants, choose File image Publish Settings image Flash image Settings image Config Constants, as shown in Figure 2-10.

image

Figure 2-10. Configuration constants for conditional compiles

As you can see, FLASH_AUTHORING is supplied automatically. You can supply as many constants as you please by clicking + in the upper-right corner.

Just like any conditional used in your code, a config constant must satisfy a requirement. As long as the condition is met, the code within the conditional statement is added to your compiled .swf file; otherwise, say goodbye to the code, because it is not added. This is the beauty of config constants versus plain old conditional statements.

Here’s an example of a config constant used around a conditional statement for conditional compiling:

if ( CONFIG::FLASH_AUTHORING == true){
force_intro();
}

The only drawback to config constants is that they’re specific to the Flash IDE. Recent releases of the FlashDevelop application include added support for config const, as well as Flash Builder.

ActionScript Editors

When you use encapsulation, the more modular your code, the more objects your system can use. Understanding their relationships is crucial in managing the objects in your application; but for ease of maintenance and development, having a good editor is very important.

Unfortunately, the Flash IDE doesn’t have the proper tools to enable effective RIA construction. The power of the IDE lies in its library and the ease of using a visual editor to develop down-and-dirty assets.

Many phenomenal Flash/Flex editors are available that warrant your attention. They include Adobe Flash Builder, FlashDevelop, IntelliJ IDEA, and—my personal favorite—FDT. Each does a terrific job of providing code hints as you type; these hints visually help you recall the available interface without having to pause your thought process.

Profiling is another great bundle. Why just debug a system, when at the same time you can see all aspects of how it’s functioning? FDT and Flash Builder tap into the sampler package and reveal the memory used, the instances currently used, and the cumulative instance count. Working in these editors gives you many tools for creating classes and can often save you time. Code completion, live warnings, and refactoring are some of the time-saving techniques that can help you get ahead of a deadline.

Finally, one of the most useful aspects of these editors is the visual representation of your project’s packages and their contents (see Figure 2-11). The days of opening and closing folders are long gone, and searching for a specific class is easy as 1, 2, 3. I don’t know where I’d be without my FDT editor.

image

Figure 2-11. The flash.sampler package and its contents, as viewed in FDT

These are just a few of the many features offered by ActionScript editors. Some of them have advantages over the others. With that said, it’s wise to use the same editor as your team members, because each editor has particular behaviors and/or exclusive features. This doesn’t mean you should merely follow your team’s lead—after you research the capabilities of each editor, take the time to discuss their benefits with your team and suggest how a particular editor can improve overall development.

Summary

The idea that sometimes things don’t go your way is important as a guide during development. You can strive for optimal flexibility, but you can only do so much to loosen the binds of your architecture while keeping it structurally sound. As revealed through the Vector class, you must make do with what your foundations allow. You must strive for loosely coupled relationships among your objects, perhaps by downcasting to Object. If this is the case, you need to use compile-time and runtime errors to help fix mismatches among Object casting. Always do what is best to keep your application on track.

Understanding the impact of an object in your language is a great strength. It enables you to realize potential weaknesses the object may have on your system. You must consider all aspects of the language you use in order to achieve the best implementation of your design patterns. By gathering information about your system and its objects, you can build efficiently.

Key Points

· Design patterns don’t include language specifics.

· The preferred manner of inheritance in ActionScript 3 is fixed inheritance.

· Design patterns require appropriate attention with consideration to memory management.

· The Flash.sampler package gives you insight into each object’s impact on memory resources.

· Pointers may prevent the garbage collector from dumping memory.

· Design patterns pass object references for delegation and modification.

· ActionScript 3 doesn’t recognize abstract classes.

· Always deselect the Automatically Declare Stage Instances option.

· Strong typing facilitates the proper use of objects in a system.

· Strong typing optimizes ActionScript bytecode.

· Avoid the as operator for up/down casts if possible.

· Use config constants for conditional compilation.

· getDefinition and getDefinitionByName return Class object.

· Special editors provide special tools for ActionScript developers.