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;
}
}
Related
I am programming in AS3 on flash develop. I am using a program called flashpunk that adds in multiple presets for classes that i can use to make it easier to program. I have an if statement that is supposed to add a new graphic when a variable equals 1. If the user presses one on their keyboard then that variable does increase by 1. But when the variable increases by 1 and the if statement is supposed to check if it is one it doesn't add a new graphic like it's supposed too. I do have the if statement and the variable in different classes and i am not sure if i can do that, here is the code. The if statement that doesn't work is the one that is supposed to add a new background1.
Chapter
package
{
import net.flashpunk.Entity;
import net.flashpunk.World;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
public class Chapter extends World
{
public var mainmenu:MainMenu;
public var background1:Background1;
public function Chapter()
{
mainmenu = new MainMenu();
add(mainmenu);
background1 = new Background1();
if(mainmenu.YesNo == 1)
{
add(background1);
}
}
}
}
MainMenu
package
{
import flash.events.TimerEvent;
import flash.utils.Timer;
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
import net.flashpunk.FP;
public class MainMenu extends Entity
{
[Embed(source = "net/MainScreen.png")]
private const SPRITE1:Class;
public var YesNo:int = 0
private var sprite1:Image = new Image(SPRITE1);
public function MainMenu()
{
graphic = sprite1;
sprite1.centerOrigin();
x = 200
y = 150
layer = 150
}
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
YesNo = YesNo + 1;
}
trace(YesNo);
}
}
}
The problem is that your code is in a World class constructor. Flashpunk's idea of game objects is that Entities are added to Worlds. Once added, World runs update() method every frame in which it also runs every Entity's update() as well. Yours if statement in a constructor will always yield false as update wasn't yet executed.
If I understand correctly you want to add background entity after user will press 1. You can do it like this:
// in MainMenu class
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
world.add(new Background1());
}
}
If you want to hold reference to this entity you could:
override public function update():void
{
if (Input.pressed(Key.DIGIT_1))
{
Chapter(world).background1 = new Background1(); // note the casting of World to Chapter
world.add(Chapter(world).background1);
}
}
I'm not terribly good at this modern object coding lark and think I may have misunderstood how this is meant to to work:
This is test.as in my Flash Builder 4.7 project called test
package
{
import flash.display.Sprite;
public class test extends Sprite
{
public var myVar:int = 10;
public function test() {
var myTestsub:testsub = new testsub;
}
}
}
And this is my testsub.as file:
package
{
public class testsub
{
public function testsub()
{
trace(myVar);
}
}
}
Flash Builder tells me that in testsub.as "Possible access of undefined variable myVar" and will not compile.
From my limited, and probably incorrect, understanding, making myVar public in test.as should allow testsub.as to use it. Can anyone explain where I am going wrong? Thank you in advance.
you need to wirte your var like this:
public class the_var
{
public function the_var
{
public static var my_var:number = 10;
}
}
and call your var in other classes like this:
public class the_test
{
public function the_test
{
trace(the_var.my_var);
}
}
There are no connections between the classes. They need a link, similar to this solution:
public class test extends Sprite
{
public var myVar:int = 10;
public function test() {
var myTestsub:testsub = new testsub(this);
}
}
public class testsub
{
public function testsub(otherClass:test)
{
trace(otherClass.myVar);
}
}
In test when creating the testsub-class we pass a long a referende to the instance of test. That makes it possible to access public functions & variables on that instance from the other class. (Observer that you need to add test-class as an import inside testsub in order to compile code.
i'm trying to create a flash game, but i'm having trouble with my classes. i'm importing the classes on the main script. they're imported okay, i've tested it all. but i dont know how to use classes inside another class
this is what i've got so far:
class Class.Player {
public static var self:MovieClip;
public static var bullet:Class.Bullet;
function controls() {
//Shoot
if (Key.isDown(Key.SPACE)) {
bullet = new Bullet(100, 100);
}
}
it loads the class and declares the variable, but it claims "Bullet" is not an existing method. it is an existing method, and it works when i call it from the main script.
also, do i need to declare the class like public static var bullet:Class.Bullet;? it's the only way that works for me but wondered if there's a better way?
It looks like you are having problems with the naming of your classes. You don't have to call your classes Class.Name. Just use Name.
In your case, you could have a file called
Player.as
With this content:
package {
import flash.display.MovieClip;
public class Player extends MovieClip {
public private var bullet:Bullet;
public function Player(){
//constructor
}
function controls() {
//Shoot
if (Key.isDown(Key.SPACE)) {
bullet = new Bullet(100, 100);
}
}
}
}
And then a file called
Bullet.as
With this content:
package {
import flash.display.MovieClip;
public class Bullet extends MovieClip {
public function Bullet(x:Number, y:Number){
//constructor
}
}
}
You can read more information about creating and using custom classes here: http://www.flashandmath.com/bridge/intro/class.html
You're putting your package information in the Class name. I have no idea how this is even compiling for you (especially given that Class is a reserved word.
Your files should look more like:
package somePackage {//somePackage instead of the reserved word Class you were using
import someOtherPackage.Bullet;
public class Player extends MovieClip {
public var bullet:Bullet;
public function Player():void {
super();
//other constructor logic here
}
}
}
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.