1

Rather than use a hard-coded switch statement where you pass it the string name of a class and it then instantiates the appropriate class, I'd like to pass the actual name of the class to my factory method and have it dynamically create an instance of that class. I thought it would be trivial and am surprised it is not working. I must be missing something quite basic:

sample code:

createProduct(50, "Product1Class");
createProduct(5, "Product2Class");


private function createProduct(amount:uint, productClassName:String):void {
    var productReference:Class;
    try {
        productReference = getDefinitionByName(productClassName) as Class;

        for (var i:uint = 0; i < amount; i++) {
        var product = new productReference() as ProductBaseClass; // throws reference error!
        }
    } catch (error:ReferenceError) {
        throw new ReferenceError(error.message + " Have you linked a library item to this class?");
    }
 }

The only thing that may be a little odd (not sure) is that these "products" are actually linked Library items (ie: I have a movieClip in the Library that has a linkage to Product1Class and another to Product2Class both of which extend ProductBaseClass, which in turn extends MovieClip.

Why the ReferenceError?

Tom Auger
  • 19,421
  • 22
  • 81
  • 104
  • I hate when this happens - the example I posted above was intended to be an abstraction of my actual real-world situation. When I ACTUALLY tested my example, it works as originally posted, no reference errors. Bloody hell. So now I'm left with trying to figure out the difference between my actual code and this working example. One obvious difference is that the library item in my real-world FLA is colour-coded GREEN. I have no idea if it's significant - what does a green colour-coded library item icon mean? – Tom Auger Jan 06 '11 at 17:50
  • Never seen a green color coded library symbol before... good luck! – Chris Bos Jan 06 '11 at 18:04
  • Okay, the green colour code just means that the linked class extends Sprite rather than MovieClip. Unfortunately this is not a fix in my particular case. – Tom Auger Jan 06 '11 at 22:53
  • The example above does not work when the product classes are not in the default package. This is the part I don't understand. If the Product1Class.as file is in the same directory as the FLA and document class (regardless of where the Factory class is) then it works as expected. The moment you put the Product1Class in its own package (for example com.test.products) then you get the reference error. What's up with that? – Tom Auger Jan 09 '11 at 02:42

3 Answers3

1

If you have a runtime loaded library then the Class's are not compiled into the main swf, so you get the runtime reference error when you try to create them.

To work around this you can declare "dummy" vars of the classes you want to compile, or if using the flex compiler there are options to include the classes you are missing.

e.g. declare these anywhere in your project

private var p1:Product1Class;
private var p2:Product2Class;

Its a frustrating problem, if your classes extend MovieClip which is a dynamic class you might be able to access the properties etc by doing something like this:

var product:MovieClip = new productReference() as MovieClip;
p1["someCustomProperty"]; //Dot notation might work here as it is a dynamic class
Chris Bos
  • 335
  • 1
  • 6
  • As far as I understand how the IDE works, this is not a runtime loaded library, but should be compiled into my main SWF (I'm not loading any external SWFs in this example). What confuses me is that typically, you can access any library item by class name as long as you set up the linkage in that library item's Properties. For example, if I export a clip called "MCMovie" for Actionscript from the linkage panel, my main document class can instantiate it thus: var myClip:MCMovie = new MCMovie(); This is basically what I'm trying to do here. – Tom Auger Jan 06 '11 at 16:21
  • 1
    So Product1Class and Product2Class are exported for actionscript? When you link the symbols, you say they are of base class ProductBaseClass (Not MovieClip) right? Otherwise ProductBaseClass would not be compiled in. Also use "var product:ProductBaseClass = new productReference() as ProductBaseClass;" – Chris Bos Jan 06 '11 at 16:38
  • Surprisingly, private var p1:Product1Class or even var p1:Product1Class = new Product1Class(); still does not resolve the issue. Now I'm wondering whether there's something else at play... – Tom Auger Jan 06 '11 at 17:05
  • Yes, they are exported for ActionScript. I'm not using Base class in the Linkage section (it defaults to MovieClip). I'm using Class to link to my specific Product1Class. This is because Product1Class and Product2Class encapsulate different functionality from each other and override base class abstract methods. – Tom Auger Jan 06 '11 at 17:09
1

Chris is absolutely right, the ReferenceError is actually being thrown during the call to getDefinitionByName, meaning that the reflection method cannot find Product1Class or Product2Class in your application domain. You can always check if a definition is available by checking the application domain directly, like:

// inside your createProduct method, yields 'false'.
ApplicationDomain.currentDomain.hasDefinition( productClassName );

Are these library assets loaded in at runtime? If so, you can either make sure that the library swf is loaded into the current application domain by adding an appropriately configured LoaderContext to your Loader, or you can replace the call to getDefinitionByName with the loaded swf's application domain's getDefinition method.

  • The library assets are embedded in the main (only) SWF, compiled through the Flash IDE. So there is no Loader, and the only LoaderContext is the main application domain. I definitely like your approach to testing hasDefinition, rather than try..catch. I will implement that change to my code, but it doesn't solve the ReferenceError. – Tom Auger Jan 06 '11 at 16:24
1

getDefinitionByName() and ApplicationDomain.currentDomain.hasDefinition() require full qualified class names. The example code in the original post works when Product1Class and Product2Class are in the default package. However, if you move the product classes to another package, you have to make sure that you are supplying the fully qualified class name to getDefinitionByName().

So if we put our product classes in com.example.products, then the call becomes:

productReference = getDefinitionByName("com.example.products.Product1Class") as Class;

I'm not really sure what the best practice is with this kind of dynamic factory class, but what I ended up doing (since all products were in the same package) was to create a constant within my factory class that defines the package for my products:

private const PRODUCT_PACKAGE:String = "com.example.products."; // note the trailing period

So that way your client code doesn't need to know (nor define) the product package. You just prepend this constant to your product class name when using getDefinitionByName().

Tom Auger
  • 19,421
  • 22
  • 81
  • 104