I have been working on an AS3 project for some while and I think that I've hit a wall. My project requires a series of elements to be arranged in a Circular List, so I copied myself in a Circular List I had done before in C#.
Problem is, that one was heavily dependant on the usage of Generics. Now I don't have those.
Here are the codes. The T variable type represents the generics that I wish existed.
Node.as:
package
{
public class Node
{
var nodeContent:T;
var nextNode:Node;
function Node(nodeElement:T)
{
this.nodeContent = nodeElement;
}
}
}
CircularList.as:
package
{
public class CircularList
{
var head:Node;
var tail:Node;
var listLength:int;
function CircularList()
{
this.head = null;
this.tail = null;
this.listLength = 0;
}
function Add(addition:T)
{
adding:Node = new Node(addition);
if(this.head == null)
{
this.head = adding;
this.tail = adding;
head.nextNode = tail;
tail.nextNode = head;
}
else
{
tail.nextNode = adding;
tail = adding;
tail.nextNode = head;
}
listLength++;
}
function Find(requested:T):Node
{
var finder:Node = null;
var searching = head;
var i:int;
while(i <= listLength)
{
if(searching.nodeContent == requested)
{
finder = searching;
}
searching = searchig.nextNode;
i++;
}
return finder;
}
}
}
Is there a way to make this thing work without the generics?
EDIT: The real problem with this is that I want the NodeContent in the Node class to be an object. I want to make a list of people sitting on a circular table, basically, but I would like to have a code that I can reuse, rather than something specifically made for this problem
From the comments it seems like your best option here would be to use an interface.
Instead of using a type have all classes T implement an interface like INode. In this interface you can define all the functionality that your type T requires and implement it as needed in each of your implementing classes. This way you can change your function signatures to take type INode instead of Class or * and have a common set of methods that these functions can act upon.
function Add(addition:INode){
//add logic on INode
}
function Find(requested:INode):Node{
//find logic on INode
}
edit: a bit of info about interfaces,
http://active.tutsplus.com/tutorials/actionscript/as3-101-oop-introduction-to-interfaces/
say we have two Classes, A, B and each of these classes have a similar method, doTrace, that needs to be implemented differently. We can define an interface, implement it in both of these classes and pass that type into any method looking to call doTrace
Start with the interface called ITraceable,
public interface ITraceable{
function doTrace():void //all methods defined in interfaces are seen as public
}
Now our two Classes, A and B
public class A implements ITraceable { //implementing our interface, when we do this we need to define all methods in ITraceable
public function doTrace():void{
trace("I am A");
}
}
Do a similar thing for B
public class B implements ITraceable {
public function doTrace():void{
trace("I am B");
}
}
Now in some outside class we want to use this
public function letsTrace():void{
doTheTrace(new A()) //I am A
doTheTrace(new B()) //I am B
}
public function doTheTrace(object:ITraceable):void { //now we can pass both A and B into this function
object.doTrace(); //since ITraceable requires all objects that implement it have this method we can guarantee it will be here
}
Hope this helps you through your application
Related
I'm trying to access properties from a class like this:
public class Oak extends Tree //base class for tree classes
{
public static const CLASS_NAME:String = getQualifiedClassName(Oak);
public var branches:Array;
public function Oak()
{
branches =
[
"Leaf1",
"Leaf2",
];
}
The class trying to access the variable is shown here:
public class TreeTest extends BaseScreen //just extends Sprite, TreeTest is shown on the stage
{
public var oak:Tree;
private var inc:int;
public function TreeTest(pModel:Model)
{
model = pModel;
model.createTree(Oak.CLASS_NAME);
oak = model.getTree(Oak.CLASS_NAME);
inc = 0;
this.addMouseClickListener(0, treeHandler); //from BaseScreen
}
private function treeHandler(e:MouseEvent):void
{
inc++;
this.displayText.text = oak.branches[inc];
}
and the model is shown below:
public class Model
{
public var treeArray:Array;
public function Model()
{
treeArray = new Array();
}
public function createTree(pClassName:String):void
{
var name:String = pClassName;
var ClassReference:Class = getDefinitionByName(name) as Class;
var classInstance:Tree = new ClassReference;
treeArray.push([name, classInstance]);
}
public function getTree(pClassName:String):Tree
{
var treeName:String = pCharacterClassName;
var match:Boolean = false;
var matchArrayRef:int = 0;
for (var i:int = 0; i < treeArray.length; i++)
{
if (match == true)
{
break
}
if (treeArray[i][0] == treeName)
{
match = true;
matchArrayRef = i;
}
else
{
match = false;
}
}
if (match == false)
{
return null;
}
else
{
return treeArray[matchArrayRef][1];
}
}
When I ran this, I got the error "Access of possibly undefined property branches through a reference with static type Tree".
After searching for a solution, I discovered that basically means that the branches array was not in the Tree class. To confirm this, if I went and took public var branches:Array away from the Oak class and put it in the Tree class, the code worked.
Why can I not access variables defined in the Oak class, and have to put everything in the Tree base class? Even if I change var classInstance:Tree to var classInstance:Oak, or even var classInstance:Object, I still get the same error if the array is in the Oak class, not the Tree class.
Like, if I create a new class Pine which would also extend Tree, do I have to put all of Pine's variables in the Tree class too?
You are referencing the oak variable as class Tree, so any property you reference from it should be of class Tree, not any subclasses, because exactly the superclass does not have them, thus compiler does not know how to address memory from branches property. Worse, this addressing might technically differ from subclass to subclass, an that's why directly addressing properties of a subclass via reference of type superclass is not possible.
There is a workaround. You can use hasOwnProperty method defined in Object class to check whether a certain tree has branches, and then address via string-based addressing like tree["branches"].
if (tree.hasOwnProperty("branches")) {
var br=tree["branches"];
trace(br); //or whatever
}
In fact, you can even go like this:
if (tree is Oak) {
var oak:Oak=tree as Oak;
// now all oaks have branches, go get em
trace(oak.branches);
}
Still, this breaks one of the object-oriented programming's core principles - you attempt to explicitly depend on subclasses having variables of certain names and are expecting them to contain values of same meaning across subclasses. Or, with the second approach, you are trying to make code know about every single subclass of Tree that will ever exist. Effectively you try to make subclasses behave as a superclass, without giving the superclass enough properties to provide that behavior in a common manner. This is not how OOP works. So, you should first devise a superclass by assigning it properties and methods of a generic Tree. All trees have branches, so branches should belong to Tree class. All trees can grow() for example, with whatever set of parameters supplied to growth, be it humidity, warmth, soil thickness, etc, but they are the same for all subclasses of Tree, be it oaks or sequoias, thus, grow() method should belong to Tree class as well. They, however, grow differently, and respond to parameters differently, but this is where override comes to help provide different behavior in subclasses.
i am trying to get an Array from a class to an other class but i can't use static or global variable for it.
in my class Jeu.as, i have 3 arrays (t_map1, t_map2 and t_map3) that represents my game map. t_map is an array that can content one of those map and a place where i can change it. I want to take the map use (form t_map) to my character (Perso.as) so it can know where it can walk or not.
The problem is that i don't know how to bring t_map from Jeu.as to Perso.as... I have tried to use a static variable (as seen in other answer) but it don't work because the map have to change...
How can i create a variable that can contain my array in my perso.as class?
in short, i want to bring t_map values form my jeu.as to an other variable in perso.as
All you really need to do is give both instances a reference to the same arrays, or give Perso a reference to Jeau. Static variables are a really bad idea, even if there's nothing inherent to this situation that would keep them from working for you.
Here's what a solution that uses Dependency Injection would look like:
package model {
public class Jeau extends EventDispatcher {
protected var _tMap1:Array = new Array();
protected var _tMap2:Array = new Array();
protected var _tMap3:Array = new Array();
//consider using more descriptive variable names
//or an array of arrays (one map in each index)
public function get tMap1():Array {
return _tMap1;
}
public function set tMap1(value:Array):void {
if (value != _tMap1) {
_tMap1 = value;
dispatchEvent(new Event('tMap1Changed'));
}
}
public function get tMap2():Array {
return _tMap2;
}
public function set tMap2(value:Array):void {
if (value != _tMap2) {
_tMap2 = value;
dispatchEvent(new Event('tMap2Changed'));
}
}
public function get tMap3():Array {
return _tMap3;
}
public function set tMap3(value:Array):void {
if (value != _tMap3) {
_tMap3 = value;
dispatchEvent(new Event('tMap3Changed'));
}
}
protected function somethingThatChangesMap1(index:int, value:String):void {
_tMap1[index] = value;
dispatchEvent(new Event('tMap1Changed'));
}
}
}
I've assumed this is a View class--you haven't given many details. You listen for events coming out of the model Class and then update the View based on whatever is in those arrays. By getting the whole instance, you have the ability to listen for these events. Otherwise, you'd have to use some other mechanism to communicate the change (such as the event bus used in Roenter link description herebotLegs).
package view {
class Perso extends MovieClip {
protected var jeau:Jeau;
public function get jeau():Jeau {
return _jeau;
}
public function set jeau(value:Jeau):void {
if (value != _jeau) {
_jeau = value;
_jeau.addEventListener('map1Changed', doMap1Stuff);
_jeau.addEventListener('map2Changed', doMap2Stuff);
_jeau.addEventListener('map3Changed', doMap3Stuff);
doMap1Stuff();
doMap2Stuff();
doMap3Stuff();
}
}
protected function doMap1Stuff(e:Event=null) {
//do actions depending on the state of map1 here
}
protected function doMap2Stuff(e:Event=null) {
//do actions depending on the state of map2 here
}
protected function doMap3Stuff(e:Event=null) {
//do actions depending on the state of map3 here
}
}
}
This is just an example of how you'd use a third Class to combine the first two. I wouldn't necessarily do it exactly like this:
package control {
public class MainGame {
protected var jeau:Jeau;
protected function perso:Perso;
public function MainGame() {
jeau = new Jeau();
//jeau setup
perso = new Perso();
perso.jeau = jeau;
}
}
}
Sounds like you need some simple accessors.
In Jeu, you'll want something to retrieve the maps like this:
function getMap(mapNumber:int):Array
{
switch(mapNumber)
{
case 1:
return t_map1;
case 2:
return t_map2;
case 3:
return t_map3;
default:
trace("Error: that's not a valid map number!")
}
}
If you saved your maps in another encompassing Array (lets call it allTheMaps), the function would look much nicer:
function getMap(mapNumber:int):Array
{
allTheMaps[mapNumber];
}
Then in Perso, you need to store a reference (or several if it needs to know about multiple maps at the same time) of an array to store the map in. You'll also need a function to set the data:
var theMap:Array;
function setMap(theMap:Array):void
{
myMap = theMap;
}
Now you can pass a map from an instance of Jeu to an instance of Perso:
var Jeu = new Jeu();
var Perso = new Perso();
...
Perso.setMap(Jeu.getMap(1));
So according to this discussion there is no constant time method for getting the size of a Dictionary object.
I'm trying to implement a wrapper which adds this functionality. Nothing fancy - just a 'numPairs' property and overridden methods to keep it updated. The problem lies in that the [] operator is used to add key/value pairs, rather than a named method, so I don't know how to override this to keep my counter updated. I could just do something like...
public function addPair(key:*, val:*):void {
this[key] = val;
numPairs++;
}
...but it'd be really nice if I could keep the bracket notation. Does anyone know of a solution?
If you want to keep the bracket notation you can still use the Proxy class for that, wrapping a real dictionary.
Here an implementation using the Proxy class, but here i didn't use a weak dictionary because it can be tricky as the 'key' can be garbaged collected and you will not be aware of that. Of course performance operation (adding, removing, ...) will also be lower than the real dictionary.
here the live test : http://wonderfl.net/c/dstz
import flash.utils.Dictionary;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class MyDict extends Proxy {
private var _size:int = 0;
private var _dict:Dictionary = new Dictionary();
public function get size():int {
return _size;
}
flash_proxy override function getProperty(name:*):* {
return _dict[name];
}
flash_proxy override function setProperty(name:*, value:*):void {
if (!_dict.hasOwnProperty(name))
_size ++;
_dict[name] = value;
}
flash_proxy override function deleteProperty(name:*):Boolean {
if (_dict.hasOwnProperty(name)) {
_size --;
delete _dict[name];
return true;
}
return false;
}
}
var dict:MyDict = new MyDict();
dict[1] = 2;
dict["foo"] = "bar";
trace(dict.size, dict[1], dict["foo"]);
delete dict[1];
trace(dict.size, dict[1], dict["foo"]);
I understand you like that you want to keep actual pair count, and you should also check for undefined/null value passed that would indicate a removal of pair. So, you first check if there is a key in "this", then assign value.
public function addPair(key:String, val:*):void {
if (this[key]) {
// pair exists, updating
this[key]=val;
if (!val) numPairs--;
} else {
// pair does not exist, adding
if (val) {
this[key]=val;
numPairs++;
}
}
}
The question is a bit silly. I am trying to implement a skill updating system. So to explain.
There is a class
class AppInfo
{
public static var power:int = 10;
public static var speed:int = 20;
}
and class SmartButton which should take a reference to one of the static variables e.g. power in a constructor and increment it on the given value.
e.g.
class SmartButton
{
public function onClick(skillReference:int = <AppInfo.power>, incrementVAlue:int = 10)
{
skillReference += incrementVAlue
}
}
I want this code to update the value of the power in AppInfo class. But this doesn't happen... I assume because the skill was passed as value not as reference...
Can you suggest a way of solving the task?
Thanks
Your assumption is correct, ints are passed by value rather than reference. One direct approach would be to encapsulate power into a reference type (a class) rather than a value type:
class Skill {
public var value:int;
public function Skill(val:int) {
this.value = val;
}
}
class AppInfo
{
public static var power:Skill = new Skill(10);
public static var speed:Skill = new Skill(20);
}
Then passing power should pass it as a reference to the instance. Though you would have to change your implemenation a bit to use skillReference.value instead.
Aside from that, I think there are a couple of ways to abstract what you want out. One way would be use an interface and leverage some dependency injection.
interface ISkills
{
function get power():int;
function set power(val:int):void;
}
class AppInfo implements ISkills
{
private static _power:int = 0;
public function get power():int { return _power; }
public function set power(val:int):void { _power = val; }
}
class SmartButton
{
public function onClick(skills:int = ISkills, skill:String = "power", incrementVAlue:int = 10)
{
skills[skill] += incrementVAlue
}
}
The idea here that you want to decouple your usage from your implementation. In this case SmartButton doesn't need to know how Skills work just how to operate on them. It loses its reference to the static class AppInfo in favor of an injectable instance. There are some advantages to this approach, it makes it easier to test and easier to swap implementations later if you decide that a static class isn't the best implementation idea without having to update a bunch of classes/code. Also, rather than injecting ISkills into the method, you could inject it into the constructor of SmartButton, and keep a private reference to the skill container.
Another approach would be to use a functional approach.
class SmartButton
{
public var defaultWorker:Function = function(val:int):void {
AppInfo.power += val;
}
public function onClick(worker:Function = undefined, incrementValue:int = 10):void
{
if(worker == undefined) worker = defaultWorker;
worker.call(this, incrementValue);
}
}
Again, in this case, rather than tightly coupling your implementation to use the AppInfo class directly, you inject a "worker" for it do the work for you (if the worker is undefined then use the default worker. You can then swap out which property gets changed by changing the closure that gets passed in. For instance if you wanted to change speed instead then you would call:
var smartButton:SmartButton;
smartButton.onClick(function(val:int):void { AppInfo.speed += val});
Not quite as succinct as it could be, but it gets the job done.
The obligatory "elegantly sophisticated" approach using the command pattern:
Interface Command {
function execute():void;
}
Class UpdatePower implements Command {
private var appInfo:AppInfo;
private var delta:int;
public function UpdatePower(appInfo:AppInfo, delta:int) {
this.appInfo = appInfo;
this.delta = delta;
}
public function execute():void {
appInfo.delta += delta;
}
}
Class SmartButton {
var command:Command;
public function SmartButton(command:Command) {
this.command = command;
}
public function onClick(event:Event):void {
command.execute();
}
}
I would probably implement this in a slightly different way.
Maybe something like;
class Properties {
private var _properties:Dictionary = new Dictionary();
public function setValue(key:String, value:int) {
_properties[key] = value;
}
public function getValue(key:String):int {
if( !_properties[key] ) return 0;
else return _properties[key];
}
public function modifyValue(key:String, value:int) {
setValue(key, getValue(key) + value);
}
}
class SmartButton
{
public function onClick(target:Properties, key:String, incrementValue:int = 10) {
target.modifyValue(key, incrementValue);
}
}
Or something along those lines.
If an AS3 method returns a reference to a complex type, is there any way to make that 'readonly', like how you can have const member functions in C++? An architecture I want to use calls for a class building itself from a passed template object... and really the template object should not be modifiable. I'm currently forced to add call-back enumerators and/or lots of extra accessor methods.
Flex has an ObjectUtil.clone() method that will make a deep copy. The copy will still by modifiable, but since it's a copy, the changes won't propagate back to the original.
The method is no complicated so if you're not using Flex, just add this to a util class:
public static function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
There is no way to do that in AS3, there is Sam's way of doing it, but it still requires copying that object before you return it, depending on the complexity of that object, it can impact the performance.
Immutable interfaces are a near-equivillant to const-correctness. Here's an example:
interface CPoint {
function get x():Number;
function get y():Number;
}
class Point implements CPoint {
private var _x:Number;
private var _y:Number;
public function get x():Number { return _x; }
public function get y():Number { return _y; }
public function set x(val:Number) { _x = val; }
public function set y(val:Number) { _y = val; }
public function normalize():void {
var length:Number = Math.sqrt(_x*_x + _y*_y);
_x /= length;
_y /= length;
}
public function Point(x:Number, y:Number) {
_x = x; _y = y;
}
}
If you return a Point as a CPoint reference, then its fields cannot be altered. You can do an explicit cast to a Point from a CPoint to force access, but you can do the same thing with const casting in C++.
Unfortunately, AS3 doesn't support covariance like it should, so things get unnecessarily difficult for const sub-objects. For example, if you had a Line class that was made up of two points, you might want to say line.start.x = 47; if you have full access to the line, but allow reading of line.start.x through an immutable interface. You could do this if there was covariance, but instead you'll need to add separate get properties for mutable and immutable properties. So, you'd end up instead with line.cstart.x for reads from a CLine. Something like this:
interface CLine {
function get cstart():CPoint;
function get cend():CPoint;
}
class Line implements CLine {
private var _end:Point;
private var _start:Point;
public function get cend():CPoint { return _end; }
public function get cstart():CPoint { return _start; }
public function get end():Point { return _end; }
public function get start():Point { return _start; }
public function Line(x1:Number, y1:Number, x2:Number, y2:Number) {
_start = new Point(x1, y1);
_end = new Point(x2, y2);
}
}
I would create a flash.utils.proxy object. You could create a proxy object that has read only implementation of a child that is passed in.
Here is the documentation for creating a proxy object. http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Proxy.html
Note: Proxy is pretty damn slow, since you'll be bypassing native object checking, and replacing it with a function call -- which when using a lot will be slow. I would do some simple performance testing first.
note: This is pseudo-code.
use namespace flash_proxy;
dynamic class ReadOnly extends flash.utils.Proxy {
private var target:Object;
public function ReadOnly(target:Object) {
this.target = target;
}
flash_proxy function getProperty(name:*):*
return target[name];
}
flash_proxy function setProperty(name:*, value:*):void
// throw an error or do nothing
}
}
You could then do:
var readOnly:ReadOnly = new ReadOnly(stage.loaderInfo.parameters);
readOnly.someparameter = 'newvalue';
trace(readOnly.someparameter); // should be old value