Limit argument type to package, possible? - actionscript-3

In AS3, the below method accepts a parameter of any type:
public function myFunc(data:*) :void
Is it possible to limit the type to a specific package? Something like this maybe:
public function myFunc(data:(my.package:*)) //Accepts any type from my.package

It is possible, but will only have type control at runtime.
import flash.utils.getQualifiedClassName;
public function myFunc(data:*):void {
if (data is Object) {
var fqcn:String=getQualifiedClassName(data);
if (fqcn.slice(0,10)!='my.package') return; // otherwise work
// work here
} // simple types process if needed
}

This sounds like a design issue. One way to make this work during compile is if the parameter type is a custom class:
public function myFunc(data:MyCustomClass):void
Assuming that all the classes within my.package are varied, you could create a custom base class that extends Object and have all of your classes within my.package extend from this base class. Of course, If, however, the inheritance of your my.package classes is less broad you wouldn't need to reach so far. For example, you should only extend from DisplayObject if all the classes within my.package are of that type.
There may also be a way to accomplish what you want using namespaces, but I'm unsure.

Related

AS3: inline way to write get and set?

I was reading a tutorial and the user there was starting setters and getters like so
function get f():Number;
function set f(value:Number):void;
inside an interface, then saving it in the main file inside a variable
var testNode:INode;
and referencing them as
testNode.f;
I thought this was really handy, specially for when you have a lot of variables. Instead of having to create two functions for each private value.
However, I tried to do the same without instancing the get and set inside an interface (because I had no need for this) and I get an error saying the function doesn't have a body.
Why is that? Is there any way to write get and set in such a clean, short manner? So far I've been writing these as
public function get someVar():SomeClass {
return _someVar;
}
public function set someVar(newValue:SomeClass):void {
_someVar = newValue;
}
but it's a bit of a hassle when I have several private variables.
An interface is just a model for your class, it does not allows you to skip the definition of a function (or a getter/setter). You must declare the getter and the setter in any class implementing your interface. What you are looking for is an extension (inheritage).
If you define a class Mother.as with a getter and a setter, the class Child.as which extends Mother.as doesn't need to redefine the getter and setter.
Furthermore, if you don't plan on writing anything else inside the getter and setter, you should use a public variable. These are less resource consuming than useless get/set.

Passing Model to View constructor for Stage Instances

I'm looking for an elegant solution to setting the Model argument for a View that is a stage instance.
My View constructor looks like this:
public function View($model:Model, $controller:IController=null){ ... }
My Subclass constructor:
public function ViewSubClass($model:Model, $controller:IController=null){ ... }
The idea is that the subclass will be a UI element that I'd rather just have on stage and not have to position it manually. I've thrown around the possibility of having placeholder elements that get removed and using their positions to attach the real UI elements but it seems a little hackish to me.
I suppose I could always set the default value of the $model argument in View to null, but not all views will be stage instances so I don't exactly want to do that either.
Any thoughts are appreciated.
Flash has no way to populate constructor parameters. This is why I always suggest avoiding constructor parameters for View. If this is your own Class, I'd suggest to go ahead and fix the parent Class so it doesn't need this.
If it is not, you can use
public function VewSubClass() {
super(null, null);
}
I would be concerned here that you actually need the Model and controller, so I would make setters available on the View subclass and then populate those variables once the instance arrives on stage.
public function set model(value:Model):void {
_model = value;
//do whatever the super constructor did based on receiving the model
}
public function set controller(value:IController):void {
_controller = value;
//etc.
}
Note that it probably shouldn't be necessary for the View to know about the controller, and I wouldn't suggest that the entire model be given to the View either--just the few properties it needs. So if the Class is yours, that puts you into a better position to correct these (IMO) architectural problems.
Based on the signature you've provided, I wouldn't be at all surprised to find that the model and controller internal storage are private, rather than protected, which means you're pretty much faced with a rewrite anyway (assuming the model and controller are needed on the View).
I solved the issue by changing the ViewSubClass constructor to:
public function ViewSubClass ($model:Model=null, $controller:IController=null){ ... }
Not sure why I was thinking that the constructor had to match the superclass' constructor perfectly. I'm still open to more elegant solutions if there are any. Thanks for looking.
-Veo

AS3 smarter type definitons

I'm working on a component based engine in AS3 and I've got a function in game object that returns a component based on it's type:
gameObject.Has(Body); //This will return a reference to the gameobjects body component
The problem I'm having is accessing the component. To do so I have to do something like this:
Body(gameObject.Has(Body)).SetVelocity(5);
Does anyone have a better way of doing this?
Edit:
public function Has(type:Class):BaseComponent
{
for each(var component:BaseComponent in m_components)
if (component is type)
return component;
return null;
}
What do you currently have for the return type of Has()?
If you prefer to circumvent type checking. You can have your Has() method return type Object.
public function Has():Object{
...
return anObject;
}
#then you can call any prop/method without throwing type errors.
gameObject.Has(Body).SetVelocity(5)
There is not however a nice way to maintain type checking without casting the var, which you're already doing in your example code.
It seems like your issue is that you're trying to be overly generic in your implementation. This introduces two possible points of failure: on the "in" side, where you could potentially load in a BaseObject for your Body that is not a Body, and on the "out" side, where your gameObject could return an object that is not a Body (the fact that the current implementation is apparently type safe is not a given).
You know that any time you are calling what is essentially a really generic getter that you need it to be returning a specific type of object (because you'll be calling methods on it), so why not make life easier on yourself by just making the explicit getter and setter needed?
Or you could get totally ridiculous and genericize your BaseObject like:
gameObject.has(Body).callMethod('setVelocity').to(5);
We are doing kind of the same thing in our project. But we have another approuch. We have a utility class in our ObjectUtility. The method returns all objects under a specific root object.
public function collectObjectsByType(object:Object, type:Class, results:Array):void
{
}
I can just show the method struct.

AS3 - Parametrized Factory method using actual class name

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?
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 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.
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().

trouble accessing non-static functions from static functions in AS3

I have a class containing, among other things, a drop down menu. With the aim of saving space, and since the contents of the menu will never change, I've made a static DataProvider for the whole class that populates each instances menu. I was hoping to populate the list with actual functions like so:
tmpArr.push({label:"Details...", funct:openDetailsMenu, args:""});
and then assign tmpArr to the DataProvider. Because the DataProvider is static the function that contains that code also needs to be static, but the functions in the array are non-static. At first it didn't seem like a problem, because when the user clicks on a menu item the drop down menu can call a non-static "executeFunction(funct, args)" on its parent. However, when I try to compile, the static function setting up the DataProvider it can't find the non-static functions being passed. If the compiler would just trust me the code would work fine!
The simple solution is to just pass strings and use a switch statement to call functions based on that, but that's big, ugly, inelegant, and difficult to maintain, especially if something inherits from this class.
The simpler solution is to just make the DataProvider non-static, but I'm wondering if anyone else has a good way of dealing with this? Making the static function able to see its non-static brethren?
Thanks.
OK, the basic reason for making things static is if you want to make it independant of an instance, for example the Math functions in as3 (you call Math.min() as opposed to var math = new Math(); math.min()...) this is useful for reference, repetetive calculation, simple actions (add 10 to x value) etc.
the problem with combining static and non static functionality is that when calling a static function, there is a possibility that the class has no instance at that point, or (in this case) that there is any reference to the function that would make sense in compilation (if a seperate class called the function, how would it reference openDetailsMenu?).
what you need to do is either go through getting function by name (object"functionname" works for example), make annonymous functions in your array or alternatively add a callback method to your static function something similar to this:
public static function doAction(object:Menu, event:String){
if(event == "details") object.openDetailsMenu() ;
}
all in all you are just adding layers of complexity that isnt really going to help. if you just add a class function and get them all to do the same action it is not taking more space or effort than if you are calling to a static function. you need to think about how and why the function is going to be used in (or out of) the class.
you could just store a static reference to the instance, in this case _instance. ( Kind of like a ghetto singleton ) just be careful not to call the static method before the class has been instantiated.
/// in your constructor define a static reference handle to the instance
public function ClassName(){
_instance = this;
}
public static function doSomethingStatic(){
var varValue = ClassName._instance.someInstanceVariable;
}