Saturday, February 23, 2008

Javascript : Creating a queue system part 2 :OOP way

In the last entry, we have seen what was a queue and how to implement it in javascript, in a procedural way. We also noticed that we were limited in our possible use of the program as multiple queues were not possible. We will see how moving from procedural to object oriented approach will allow us to deal with multiple queues in a simple way.

Reviewing the queue code


Before we go OOP, let's review the code first:
var q      = [];
var paused = false;

function queue() {
    for(var i=0;i < arguments.length;i++)
       q.push(arguments[i]);
}
function dequeue() {
     if(!empty()) q.pop();
}
function next() {
     if(empty()) return;
     if(paused)   return;
     q.shift()();
}
function empty() {
     if(q.length==0) return true;
     return false;
}
function clear() {
    q=[];
}

//application aware flush function:
//update the div with the length of the queue

function updateDIV(delay) {

 function delayed() {
   document.getElementById('queued').innerHTML+=q.length;
   next();
 }
 setTimeout(delayed,delay);
}
function flush(delay) {
 updateDIV(delay);
}
You can always read the firt part to get a better understanding of this code.

Limits of the above code


The main drawback of this programming style is that we hold our main data, the queue array and the pause flag, in the gobal space. Therefore we cannot create several queue with the above code.
The main advantage of putting everything in the global space is that the code is straight forward:
queue(function(){updateDIV(300)},function() {updateDIV(500)},function(updateDIV(450)));
dequeue();
next();
paused=true;
flush();
As you can see, we don't really have to care about the queue array, we just let the program deal with it for us. Now, let's see how we can try to implement multiple queues.

Multiple queues, the procedural way

In order to deal with multiple queues, we first need to allow multiple queue container.
Therefore, we need to change our global queue as being a parameter of the function:
function queue(q) {
    for(var i=1;i < arguments.length;i++)
       q.push(arguments[i]);
    return q;
}
function dequeue(q) {
     if(!empty()) q.pop();
     return q;
}
function next(q) {
     if(empty(q)) return q;
     q.shift()();
     return q;
}
function empty(q) {
     if(q.length==0) return true;
     return false;
}
function clear(q) {
    q=[]; 
    return q;
}

//application aware flush function:
//update the div with the length of the queue

function updateDIV(q,delay) {

 function delayed() {
   document.getElementById('queued').innerHTML+=q.length;
   next(q);
 }
 setTimeout(delayed,delay);
}
function flush(q,delay) {
 updateDIV(q,delay);
}
huummmm, as you can see, this is a little bit trickier...
I have dropped the paused flag in order to keep thing simple.
Let's see how we can use the above code:
var q1=[];
var q2=[];

q1= queue(q1,function(){updateDIV(q1,300)},function() {q1,updateDIV(500)},function(q1,updateDIV(450)));
q1= dequeue(q1);
q1 = next(q1);
flush(q1);

q2= queue(q2,function(){updateDIV(q2,300)},function() {q2,updateDIV(500)},function(q2,updateDIV(450)));
q2= dequeue(q2);
q2 = next(q2);
flush(q2);
Hummmm,,,
This is starting to be very hard!
Before, as we only had one and only one queue, we didn't have to care about which queue we were working on.
Now that we can have several queues, we need to indicate each time on which queue we are working to the function and we need to get back the queue to stay sync with the application...
in other words, this is a real pain in the butt!
Why is it that hard?
Could we simplify this??
Yes, we can, thanks to an object oriented approach!

The object oriented approach of the queue


So why the code get so difficult to write and certainly, in a long run, to maintain??
Because we are thinking in a procedural way:

We have functions that work on a particular data.


By thinking this way, we need to specify what is the data to the functions.
Depending on what you want to do, a procedural approach will be revealant, don't think that it is a wrong way of doing things (even if hardcore oop programmer will claim that even a if else statement is devil,,,just forget about them!!)

So how should we think the problem?
If we change our way of thinking, we can move to the following:

We have a particular data that possesses,has different methods.

And that's all!
Instead of thinking of different individual functions that work on a particular data, we need to think that we have a particular data that share, have particular functions/methods.
By simply inversing the first axiom,the subject of the action, we will be able to have the easyness of a global variable and the power of multiple data, ie, multiple queues.

The object in OOP


So, that's nice, we have inversed the problem and so what??
When we created our queue, we had a queue container, a queue pause flag and functions that work on the data.
If you remember the first approach, we first created our queue container, our pause flag and then called the different functions that were all working on the same data, the global queue and global pause flag.
Using an object approach, we are just going to allow to create several queue container and flag, each hold within a box, an object. Therefore each object will have it's own internal state of the data.
Come to think of it, if you have two identical TVs, one in your living room, one in your room.
If you turn on the TV in your living room, the one in your room is not going to start and hopefully.
You have two objects and each of them although they do the same thing, have their own state of information, their own data.
We are going to learn how to create TVs!!

Understanding the queue in a OOP way


Let's see how we will write the code in a OOP way:
var q1 = new Queue();
q1.queue(function(){updateDIV(300)},function() {updateDIV(500)},function(updateDIV(450)));
q1.dequeue();
q1.next();
q1.flush();

var q2 = new Queue();
q2.queue(function(){updateDIV(300)},function() {updateDIV(500)},function(updateDIV(450)));
q2.dequeue();
q2.next();
q2.flush();

If you remember the procedural approach with only one global variable, you will see that is very similar, but remember:
We do not have functions that work on a particular data.
We have a set of data that has some particular functions to work on.
The main, main difference is the following line:
var q1 = new Queue();
In the procedural way, we add:
var q1 = [];
var paused=false;
hum ??
But where did you write the line defining the paused flag in the OOP way??
In fact, remember, we are working with a set of data that we enclose in a box, an object.
Whene we write 'new Queue()', in the background we have created an object that has a queue container, an array, and a paused flag set to false!
If you have your TV on and that you change of channels, you don't expect your TV in your room to change of channel too, right?
Here it is the same thing, we have turned on our queue, set our initial state of information (an empty queue, a paused flag to false) and get an image back, in fact, an object holding all this information.
If you have never done OOP until now, you need to know that the return value is not a string or an array, it is what we call an object, which is yet an other structure of data.
We are lucky because in javascript creating an object is the same has using an hash, or associative array!
So we get back an hash that holds the following info:
var q1 = new Queue();
q1.toSource(); //=> {  queue:[],  paused:false };
But this hash is a little bit different from a simple hash.
If you buy exactly the same TV in a store, they will share exactly the same properties, the same features but if you need to repair one of them, you need to have a way to differentiate them, this can be a code barre or a client code.
Therefore, when you create a new object in javascript, you will get back a hash, that contains the name of the object, here Queue and obviously a particular memory address.
So when you do the following:
var q1 = new Queue();
var q2 = new Queue();
var q3 = new Queue();
You are creating each time a new Queue hash that has a particular memory address with their own state of the information.

And what the heck with the rest of the code????
var q1 = new Queue();
q1.queue(function(){updateDIV(300)},function() {updateDIV(500)},function(updateDIV(450)));
q1.dequeue();
q1.next();
q1.flush();
How can we call functions on the q1 variable??
Again, remember, we do not have functions that work on a particular data, we have a set of data that has some functions.
In a OOP language, we don't refer to functions but to methods.
Each new queue container array and paused flag are not variables but properties.
Properties are state, methods are actions.
A human being has the following property : 2 legs, 2 eyes, a mouth, 2 ears,etc. And a human being has the following methods, actions : run, walk, speak, sleep,etc. Sleeping being my favorite action of all!

So we have initialised our particular set of data, hold in an object called q1 and each time you will be calling methods (actions) on it, the methods will be working on the data of this object!
So with OOP, we can create has many queues as we want, we do not have to care about passing the queue array around or the paused flag around, everything is held within the object, within each object.
That's why our code is easier to write in the end because we do not have to deal with the state of the information, the object is doing it for us!!


Conclusion


Writting the implementation code here will make the entry too long so we will see how to implement the OOP way in a third party!!
But I hope that this entry has help you understanding the difference between a procedural approach, where functions work on data and an object oriented approach where a set of data has methods.
Understanding this will allow to ease the next entry development phase!
I hope you have seen how powerful can be a OOP approach where an object can hold has many information as you want and deal with them internally without having you to care!
So keep on reading!

2 comments:

vanessa said...

thank you for these articles, they were very useful to me!

Diego said...

perfect !!!