Closed Thread Icon

Topic awaiting preservation: OOP problem, pushing around methods while maintaining references. (Page 1 of 1) Pages that link to <a href="https://ozoneasylum.com/backlink?for=27293" title="Pages that link to Topic awaiting preservation: OOP problem, pushing around methods while maintaining references. (Page 1 of 1)" rel="nofollow" >Topic awaiting preservation: OOP problem, pushing around methods while maintaining references. <span class="small">(Page 1 of 1)</span>\

 
TwoD
Bipolar (III) Inmate

From: Sweden
Insane since: Aug 2004

posted posted 01-09-2006 21:29

I've got a rather complex problem, well the problem itself is probably not so complex but the code structure is.

I'm using a queue system where I send a function (the object itself) to an Add_Action function along with some arguments.
Add_Action assumes the first argument is the function to be queued and treats any following argunents as meant for that function.
In Add_Actiom I create a new object with the method Main_Code, which points to the function I want to add.
There is also an array called arguments, which contains the rest of the arguments passed to Add_Action.
This object is what is placed in the queue array to keep function and arguments together.
Note: When I talk about an Action or Task in the queue I mean the one in Data.currentAction. I did not use to have the first Action separate
but have so now for various reasons.

Run_Actions will later execute these Actions one by one. If an Action returns true, it is removed from the queue and next Action is run by calling Run_Actions again with a setTimeout(). If it returns false, the Action stays in the queue and is run again, but the waittime is longer.

The queue is initialized with a single action in it called Decide. It simply decides which Action(s) to queue and always adds itself at the end. It always returns true to not queue more than one set of actions before a new decicion is made.

The Actions themselves can add sub-Actions by calling First_Action in the same manner as Add_Action. The difference is that the sub-Action will be put directly after the currently executing Action.

I have also put in an "Objectives" system which allows me to have a second list of Actions (called Tasks) to be executed. The Objective is represented by an object containing a set of methods and public properties. One of the methods is Run. It takes the first not-completed-Task in the Task list and adds it to the Actions queue using First_Action. The Tasks themselves are also objects containing both private and public properties and methods (created using a constructor and the new statement when added to the TaskList). The most important of the methods is Tick. That method is what's actually added to the Actions queue by Run via First_Action.

This allows the same Action to perform a different set of actions each time it is run. If the Task is completed, the next Task will be added to the queue when the same Action is run, otherwise it will add the same Task again. Once all Tasks have been run, the Objective itself recieves the status "Done" by setting that property to true. (This is also how Tasks are marked as completed)

Now to the problem:
When a Task is added to the queue, the method Tick still requires access to the public and private propertis created when constructing the Task object, since these are different depending on the Task. But it seems as if I can only access the object created in First_Action (see description of Add_Action), the one that contains Main_Code and arguments and is added to the queue.

I thought setting a variable=a method would only return a handle to that method and preserve all references etc.
But when the code in Tick runs (as the method named Main_Code), everything I reference to using this in the Task constructor gets redírected to the object containing Main_Code.

Why? How did the method forget where it comes from?

Here's the example code, only the basic structure is included so much may seem obsolete.

code:
Data={
	Actions:[],
	currentAction=false,
	Objectives : {
		Obj1 : {
			Done : false,
			Tasks : [new Task1(2,187),new Task2(1,74),new Task1(0,35)], // The list of Tasks to do before this Objective is completed and the arguments needed to customize them.
			Run : function(){ // This method is executed 
				var noTask=true;
				for(var i=0;i<this.Tasks.length;i++){
					if(!this.Tasks[i].Done){
						First_Action(this.Tasks[i].Tick);
						noTask=false
						break;
					}
				}
				if(noTask){
					this.Done=true
					for(var i=0;i<this.Tasks.length;i++){
						this.Task[i].Reset()
					}
				}
			}
		}
	},
	
}

function Add_Action(func){
	var action = { // this is the object to which Tick keeps accessing.
		Main_Code : func,
		arguments : []
	}
	for(var arg=1;arg<arguments.length;arg++){
		action.arguments.push(arguments[arg])
	}
	Data.Actions.push(action)
}

function First_Action(){
	// Does the same thing as Add_Action but puts the action at beginning of the list.
	// Enables me to queue sub-actions directly after currentAction.
}


function Run_Actions(){
	if(!Data.currentAction){
		Data.currentAction=Data.Actions.shift()
	}
	if(Data.currentAction){
		if(Data.currentAction.Main_Code()){
			Data.currentAction=Data.Actions.shift()
			if(Data.currentAction){
				setTimeout("Run_Actions()",10)
			}
		}
		else{
			Write_Log("Waiting...")
		 	setTimeout("Run_Actions()",100)
		}
	}
}

function Decide(){	// This Action is the "main loop" which decides what to do by queueing up Actions.
	if(Math.random()>0.5){ // 
		Add_Action(Action1,Math.random())
	}
	else{
		Add_Action(Action2)	// When running this Action it will try to run 
	}
	Add_Action(Decide) // Make a new decicion after the queue has been emptied.
	return true
}

function Action1(){
	if(Data.currentAction.arguments[0]>0.5){ // Retrieve the argument sent in the Decide function.
		return true // This Action has been performed successfully
	}
	return false // Dang, no luck this time, retry this Action after waiting some time.
}

function Action2(){
	if(!Data.Objectives.Obj1.Done){ // This Action triggers an objective and runs it Task by task until it has ended.
		Data.Objectives.Obj1.Run() // New Actions will be placed in the queue one or more at a time by the objective.
	}
	alert("Objective completed!")
	return true
}


function Task#(arg1,arg2){
	var private1=arg1 // These are the properties I want to access when Tick is placed in the Queue as the method Main_Code.
	var private2=arg2
	this.Done=false;
	this.Tick=function(){ // This is the function that will be queued and run later in Run_Actions().
		private1+=Math.random()
		private2-=Math.random()
		if(private1>5 || private2<5){
		        this.Done=true; // This Task is done, skip it when trying to complete the Objective next time.
			// As it is now, setting this has no effect since it only changes Data.currentAction.Done instead of Data.Objectives.Obj1.Tasks[#].Done
		}
		return true;
	}
}

Add_Action(Decide)
Run_Actions()


Note that this is not the actual code used and that I wrote it of the top of my head so syntax errors are likely, but the logic should be the same.

/TwoD

Tyberius Prime
Paranoid (IV) Mad Scientist with Finglongers

From: Germany
Insane since: Sep 2001

posted posted 01-09-2006 22:05

how about passing your Task in with as an argument to the func in add_action()/first_action (which probably should be prepend_action ;-)?
so instead of this.Tick = function() you have this.Tick = function (self) and do self.Done = true insteaf of this.Done = true;

Further up you get First_Action(this.Tasks[i].Tick, this.Tasks[i]); instead of just
First_Action(this.Tasks[i].Tick);


Am I making sense?

TwoD
Bipolar (III) Inmate

From: Sweden
Insane since: Aug 2004

posted posted 01-09-2006 22:19

Yes you are making sense.
I'll try that later, thanks for the quick reply!

/TwoD

TwoD
Bipolar (III) Inmate

From: Sweden
Insane since: Aug 2004

posted posted 01-10-2006 22:20

Yes it works.
But I need to do self=Data.currentAction.arguments[0] since the arguments are not passed directly, don't think that's possible since the number of arguments varies depending on the action.
But I can only use the self-reference for public properties and methods.
I still have no way of accessing my private properties unless I make them public too...

/TwoD

Scott
Paranoid (IV) Inmate

From: schillmania.com
Insane since: Jul 2002

posted posted 01-17-2006 04:12

I probably didn't read through your code thoroughly enough, but I typically work around "this" scope issues via closures:

code:
var SomeObject(oImg) {
  var self = this;
  this.o = oImg;
  this._property = false;
  this._mouseover = function() {
   // "this" == oImg due to event handler, however
    self._property = true;
  }
  this.o.onmouseover = this._mouseover;
}

var so = new SomeObject(someImage);



Another way you may be able to get around the scope issue via the apply() method - eg. so.mouseover(so) would execute with "this" being the scope of "so", not the image.

TwoD
Bipolar (III) Inmate

From: Sweden
Insane since: Aug 2004

posted posted 01-20-2006 00:33

Scott: Either you didn't understand my post, or I don't understand yours in comparison to mine... don't know which lol.

My problem is that I can't access the variables defined with "var" in a constructor from methods in the same constructor. This is because the method is copied, or rather a reference to it, into another object and executed from there. this when used in the method no longer points to the object created by the constructor, but to the second temporary object called Data.currentAction.
I solved this by passing the object created by the constructor as an argument to the method as suggested by TB.

In your example I'd not be able to access the self object since the method is "moved" from SomeObject to Data.currentAction.
self._property = true would throw an error saying that self has no properties or that it doesn't exist.

/TwoD

« BackwardsOnwards »

Show Forum Drop Down Menu