So, how do we write a class over several files in action script 3?
In C# there's the "partial" keyword.
In C++ it's natural (you just "#include ..." all files).
In Flex 3, in a component, you add this tag: <mx:Script source="myfile.as"/>.
How do I split the following class into several files;
package package_path
{
public class cSplitMeClass
{
public function cSplitMeClass()
{
}
public function doX():void
{
// ....
}
public function doY():void
{
// ....
}
}
}
For example I want to have the doX() and doY() functions implemented in another ".as" file.
Can I do this?
And please, don't tell me something like "a good practice is to have them in one file" :)
As per your request, I'm sparing you the "best practices lecture". So, I'll just say there's an include directive in AS 3.0, which could help you out here.
Basically, you can do:
package package_path
{
public class cSplitMeClass
{
public function cSplitMeClass()
{
}
include "the_file_where_doX_and_doY_live.as"
}
}
And then in "the_file_where_doX_and_doY_live.as"
public function doX():void
{
// ....
}
public function doY():void
{
// ....
}
You could do it with inheritance:
// file: cSplitMe1.as
class cSplitMe1 {
function doX() {
// ...
}
// file: cSplitMe2.as
class cSplitMe2 extends cSplitMe1 {
function doY() {
// ...
}
// file: cSplitMe.as
class cSplitMe extends cSplitMe2 {
function cSplitMe() {
doX();
doY();
}
}
It's good practice, nothing wrong with that.
There's the import keyword. Example:
import Class_A.as;
import Class_B.as;
Now of course in order to use them, you need to declare them, preferably in your constructor.
public function Class_C()
{
//Create a new instance of Class_A
var objA:Object = new Class_A("parameter one", "parameter two");
//Create a new instance of Class_B
var objA:Object = new Class_B("parameter one", "parameter two");
}
Of course it all depends on how you're going to make this work. I would also suggest you use a Main class from where you could run your code. I guess you already knew that though.
Good luck.
Related
Does this work well as a Singleton in actionscript? i.e. are there any gotchas I should be aware of, and is it true that only one instance of AVManager would be created in this case:
Note that I do get the expected output (only one time of "instantiated first time" and numbers follow sequence):
instantiated first time! 1
FILE 1: 2
FILE 2: 3
and finally 4
Here are the files....
AV_CONFIG.as:
package {
public class AV_CONFIG {
public static var AVM:AVManager = new AVManager();
}
}
AVManager.as:
package {
import flash.events.EventDispatcher;
public class AVManager extends EventDispatcher {
public var someConstantData:uint = 1;
public function AVManager() {
trace('instantiated first time!', someConstantData);
}
}
}
Then:
File1.as:
package {
import AV_CONFIG;
import flash.display.Sprite;
public class File1 extends Sprite {
public function File1() {
AV_CONFIG.AVM.someConstantData++
trace('FILE 1:', AV_CONFIG.AVM.someConstantData);
}
}
}
File2.as:
package {
import AV_CONFIG;
import flash.display.Sprite;
public class File2 extends Sprite {
public function File2() {
AV_CONFIG.AVM.someConstantData++
trace('FILE 2:', AV_CONFIG.AVM.someConstantData);
}
}
}
Main.as (the DocumentClass):
package {
import AV_CONFIG;
import flash.display.Sprite;
public class Main extends Sprite {
public function Main() {
var f1:File1 = new File1();
var f2:File2 = new File2();
AV_CONFIG.AVM.someConstantData++
trace('and finally', AV_CONFIG.AVM.someConstantData);
}
}
}
Generally with singletons you want to:
Limit or dissolve the ability to create instances of that class.
Create a means of getting an instance of that class statically.
Example:
public class AvManager
{
private static var _instance:AvManager;
internal static var created:Boolean = false;
public function AvManager()
{
if(AvManager.created)
{
throw new Error("Cannot created instances of AvManager, use AvManager.instance.");
}
AvManager.created = true;
}
public static function get instance():AvManager
{
if(_instance == null)
{
_instance = new AvManager();
}
return _instance;
}
public function test():void
{
trace("Working.");
}
}
Where you could now use:
AvManager.instance.test(); // Working.
The biggest gotcha is allowing global access to something if its state can be changed. If this is a project where you expect the codebase to be maintained for longer than a week or so and you think it's likely to have more than 500 lines of code, I'd strongly suggest avoiding doing this--I can tell you from experience that in a large project it can be difficult to impossible to figure out which of the hundreds of Classes that have access to your Singleton made the change to its state that is causing a given bug.
Next, requirements have a way of changing. What if you suddenly need 2 AVManagers? You'll find that you've created so many baked-in references to your static that changing it will blow the entire project to heck. Again, I speak from experience here. If you use dependency injection (which is just a scary way of saying that Classes that need an AVManager have a property that is populated from the outside), then these types of changes become easy...just give them a different AVManager, done.
Finally, if you have any pretensions of wanting to do Test Driven Development, using globals/statics in this way will essentially make all that code untestable. You can't provide an alternate AVManager for testing, since all the Classes with a dependency on it are hard-wired to go get that specific one.
Good luck!
Yeah this works fine, a different method is to put an AVManager right in its own class file at the top:
private static var AVM:AVManager = new AVManager();
and get it when required with a function in the AVManager class like this:
public static function GetInstance():AVManager {
return AVM;
}
This setup isn't necessary but provides some nice little protection by disallowing direct access.
Good luck with your project.
See this code as an attempt for creating something different.
An other Singleton in AS3 :
First an Interface :
package test {
public interface Foo {
function func0():void;
function func1(arg:String):String;
}
}
And then a Singleton :
package test {
public class BASIC_FOO {
public static const BASIC_FOO:Foo = new BasicFoo();
}
}
import test.Foo;
class BasicFoo implements Foo {
public function func0():void {
}
public function func1(arg:String):String {
return arg;
}
}
I would like to read the source of the flash.net.FileReference class. Is this possible? Where can I find the source files, do they come with the Adobe Flash or Flash Builder?
To read the flash package files, you can find the playerglobal.swc - change the name to playerglobal.zip and unzip the package. Then, decompile the library.swf file and get the script files. Here's what I found for FileReference:
//FileReference
package flash.net
{
import flash.events.*;
import flash.utils.*;
public class FileReference extends flash.events.EventDispatcher
{
public function FileReference()
{
super();
return;
}
public function upload(arg1:flash.net.URLRequest, arg2:String="Filedata", arg3:Boolean=false):void
{
}
private function _load(arg1:flash.utils.ByteArray):void
{
}
public function load():void
{
this._load(new ByteArray());
return;
}
public function get size():uint
{
}
public function get type():String
{
}
public function browse(arg1:Array=null):Boolean
{
}
public function get name():String
{
}
public function get creator():String
{
}
public function get creationDate():Date
{
}
public function download(arg1:flash.net.URLRequest, arg2:String=null):void
{
}
public function get modificationDate():Date
{
}
public function get data():flash.utils.ByteArray
{
}
public function cancel():void
{
}
private function _save(arg1:flash.utils.ByteArray, arg2:String):void
{
}
public function save(arg1:*, arg2:String=null):void
{
var defaultFileName:String=null;
var data:*;
var d:flash.utils.ByteArray;
var loc1:*;
data = arg1;
defaultFileName = arg2;
d = new ByteArray();
if (data == null)
{
throw new ArgumentError("data");
}
if (data is String)
{
d.writeUTFBytes(data as String);
}
else if (data is XML)
{
d.writeUTFBytes((data as XML).toXMLString());
}
else if (data is ByteArray)
{
d.writeBytes(data as ByteArray);
}
else
{
try
{
d.writeUTFBytes(data);
}
catch (e:Error)
{
throw new ArgumentError("data");
}
}
d.position = 0;
if (defaultFileName == null)
{
defaultFileName = "";
}
this._save(d, defaultFileName);
return;
}
}
}
I highly recommend not changing this file and rather extend it and override the functions you need to modify. Otherwise, you'll need to recompile the library.swf and create a custom playerglobal.swc.
As others mentioned you can see the sources for the Flash and Flex framework classes. The exact location will vary.
For Flash CS4 on Windows 7:
C:\Users\<your_user>\AppData\Local\Adobe\Flash CS4\en\Configuration
For Flex:
...\flex_sdk\frameworks\projects\framework\src
You CAN change any framework class you want as long as you're careful. In Flash nomenclature this is referred to as Monkey Patching. Create a class in your project with the same full package structure and class name as the framework class and the compiler will find and use your custom class instead of the framework class.
There are some complications in doing this with framework RSL's. For that see here:
How to Monkey Patch when using Flex RSLs
http://blogs.adobe.com/dloverin/2010/01/how_to_monkey_patch_when_using_flex_rsls.html
This does not apply to built-in or "intrinsic" classes. Those are built-into the player and wills till have stub code in the above source locations. You can't actually change intrinsic classes.
Any of the "stuff" that is available for you to view are located (for Win7 anyway) in C:\Users\<your_user>\AppData\Local\Adobe\Flash CS4\en\Configuration
The Flash CS4 portion might change depending on the version you have. Classes are in the Classes folder inside configuration.
Can I put two or more actionscript classes in one .as file like this:
//A.as
package classes {
public class A {
public function A() {
var b:B = new B();
}
}
internal class B {
public function B() {
trace("Hello");
}
}
}
It doesn't work in Flash Builder:
A file found in a source-path can not have more than one externally
visible definition. classes:A; classes:B
If it possible, I'm going to ask next question.
Can I place two or more packages with multiple classes in one .as file?
No and no. The following works:
//A.as
package classes {
public class A {
public function A() {
var b:B = new B();
}
}
}
class B { // <--- Note the class is outside of the package definition.
public function B() {
trace("Hello");
}
}
The class B is only visible to the class A - you cannot have more than one visible class in one file (exactly what the error message states). And you cannot have more than one package in a file.
What I am trying to do is kind of odd, but I am wondering if anyone can come up with a clever way to do what I want to do. Basically, I want to re-define a named function at runtime. I can do this with anonymous functions, but I can't figure out a way to do it for named functions. I want to do this so that I can implement a "spy" functionality on an object for a testing framework (a port of Jasmine to Flex).
Take, for instance, this class:
public class TestClass
{
public var anonymous:Function = function():void {
trace("original anonymous");
};
public function named():void {
trace("original named");
}
}
I can easily re-define the anonymous function because it is just a variable. Javascript uses this idiom a lot.
var testClass:TestClass = new TestClass();
testClass.anonymous = function():void { trace("overridden anonymous"); }
BUT, when I do the same thing for named functions, you get a compile-time error:
// Does not compile
testClass.named = function():void { trace("overridden named"); }
I tried to make it a bit more "squishy" but this leads to a runtime failure "Cannot assign to a method named on TestClass".
// Compiles with runtime failure
testClass["named"] = function():void { trace("overridden named"); }
Can anyone more clever than I come up with a way to hack this? Can the bytecode be hijacked? Something?
I want to modify an object, not a
class
But object doesn't contain functions, only non-static variables. I tried to use prototype property and replace method there, but original method still gets called instead of injected one.
About "hack" bytecode, do you mean "hack" already loaded SWF in runtime? I think it's not possible. I'm sure, though, you can parse SWF with something like as3swf, find method in bytecode, replace it and save result in new SWF.
I had an idea bout making a function "cache" . This might work with what you need.
Let's say you have a class "Car" with a method you need to redefine at runtime:
public class Car extends Sprite
{
private var functionCache:Function;
public function Car()
{
super();
}
public function flexibleFunction(functionBody:*=null):void{
if(functionBody is Function){
functionBody.call();
functionCache=functionBody;
} else {
functionCache(functionBody);
}
}
}
Usage:
public class Main extends Sprite
{
private var car:Car;
public function Main()
{
car = new Car();
car.flexibleFunction(function(){trace("redefine test #1")});
car.flexibleFunction();
car.flexibleFunction(function(doParametersWork:String="let's see"){trace("redefine test #2: " + doParametersWork);});
car.flexibleFunction("yes they do");
car.flexibleFunction();
}
}
an easy way to accomplish what you want is to simply pass a new function to the original function and execute it from there:
package
{
//Imports
import flash.display.Sprite;
//Class
public class RedefineFunction extends Sprite
{
//Constructor
public function RedefineFunction()
{
originalFunction();
originalFunction(redefinedFunction);
}
//Original Function
public function originalFunction(redefinition:Function = null):void
{
if (redefinition != null)
redefinition();
else
trace("Original Function Definition");
}
//Redefined Function
private function redefinedFunction():void
{
trace("Redefined Function Definition")
}
}
}
traces:
Original Function Definition
Redefined Function Definition
How can I make an abstract class in AS3 nicely?
I've tried this:
public class AnAbstractClass
{
public function toBeImplemented():void
{
throw new NotImplementedError(); // I've created this error
}
}
public class AnConcreteClass extends AnAbstractClass
{
override public function toBeImplemented():void
{
// implementation...
}
}
But.. I don't like this way. And doesn't have compile time errors.
abstract classes are not supported by actionscript 3. see http://joshblog.net/2007/08/19/enforcing-abstract-classes-at-runtime-in-actionscript-3/
the above reference also provides a kind of hackish workaround to create abstract classes in as3.
Edit
also see http://www.kirupa.com/forum/showpost.php?s=a765fcf791afe46c5cf4c26509925cf7&p=1892533&postcount=70
Edit 2 (In response to comment)
Unfortunately, you're stuck with the runtime error. One alternative would be to have a protected constructor.... except as3 doesn't allow that either. See http://www.berniecode.com/blog/2007/11/28/proper-private-constructors-for-actionscript-30/ and http://gorillajawn.com/wordpress/2007/05/21/actionscript-3-%E2%80%93-no-private-constructor/.
You may Also find these useful: http://www.as3dp.com/category/abstract-classes/ and, in particular, http://www.as3dp.com/2009/04/07/design-pattern-principles-for-actionscript-30-the-dependency-inversion-principle/
package
{
import flash.errors.IllegalOperationError;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import flash.utils.getQualifiedSuperclassName;
public class AbstractClass
{
public function AbstractClass()
{
inspectAbstract();
}
private function inspectAbstract():void
{
var className : String = getQualifiedClassName(this);
if (getDefinitionByName(className) == AbstractClass )
{
throw new ArgumentError(
getQualifiedClassName(this) + "Class can not be instantiated.");
}
}
public function foo():void
{
throw new IllegalOperationError("Must override Concreate Class");
}
}
}
package
{
public class ConcreteClass extends AbstractClass
{
public function ConcreteClass()
{
super();
}
override public function foo() : void
{
trace("Implemented");
}
}
}
In AS3 would just use interfaces to make sure all functions are implemented at compile time.
I know it different but does the trick for an example such as the one above.
As long as they don't permit non-public constructors in actionscript, you'd have to rely on run time errors for abstract classes and singletons.