Refactoring Code in to an Object-Oriented Paradigm #3: Moving to Object-Oriented code patterns

This is article number 3 of a series of article, which simply documents my refactoring of some code I originally wrote (pretty poorly) last year. To get the gist of what’s going on. Check the original post – Refactoring Code in to an Object-Oriented Paradigm.

Moving Code to Object-Oriented Code Patterns

So now we have our group of variables under the name autoScrobbler, I said earlier that we could also include functions under this group. This is quite a good way of going, because at the moment, we’re taking up the function name stop() globally, any function that calls stop when this code is loaded will run our function.

This can cause problems for other code and ours. For other programs, it may be trying to stop something else from running, when it calls stop(), it won’t have stopped what it expects to. For our programs, that means that the other program has just stopped the auto-scrobbler when the user hasn’t asked it to!

The other nice thing about putting our functions under the same group, is that we don’t need to rely on a name for the group. If we decide that autoScrobbler is a bad name for the group later down the line, that’s fine we can change the name and everything will still work.

function AutoScrobbler() {

var userControls = "<div id=\"autoScrobbler\" style=\"background: #FFFFFF; border-top: 1px solid #000000; border-left: 1px solid #000000; position: fixed; bottom: 0; height: 50px; width: inherit;\">"+
"<input id=\"autoScrobblerStart\" type=\"button\" value=\"Start auto-scrobbling\" onclick=\"autoScrobbler.start();\" /> | <input id=\"autoScrobblerStop\" type=\"button\" value=\"Stop auto-scrobbling\" onclick=\"autoScrobbler.stop();\" />"+
"<p><span id=\"autoScrobblerScrobbleCount\">0</span> tracks scrobbled</p>"+
"</div>";
document.querySelector("#disclaimersContainer").innerHTML += userControls;
this.startElm = document.getElementById("autoScrobblerStart");
this.stopElm = document.getElementById("autoScrobblerStop");
this.loopUID = -1;
this.lastTrackUID = undefined;
this.scrobbled = 0;
this.countReport = document.getElementById("autoScrobblerTracksScrobbled");
this.start();

}

AutoScrobbler.prototype.addLatest = function() {

var tracks = document.querySelectorAll(".userSong");
this.lastTrackUID = (typeof this.lastTrackUID == "undefined") ? tracks[1].querySelector("input").value : this.lastTrackUID;
for (var i = 0; i < tracks.length; i++) {

var item = tracks[i];
if (item.querySelector("input").value == this.lastTrackUID) {

i = tracks.length;
this.lastTrackUID = tracks[0].querySelector("input").value;

} else {

item.querySelector("input").checked = true;
this.scrobbled++;

}

}
doUserScrobble();
this.countReport.innerHTML = this.scrobbled;

}


AutoScrobbler.prototype.loadThenAdd = function() {

doRadioSearch();
setTimeout(this.addLatest, 30000);

}


AutoScrobbler.prototype.start = function() {

this.loadThenAdd();
autoScrobbler.loopUID = setInterval(this.loadThenAdd, 300000);
autoScrobbler.start.disabled = true;
autoScrobbler.stop.disabled = false;

}


AutoScrobbler.prototype.stop = function() {

clearInterval(this.loopUID);
this.lastTrackUID = undefined;
this.loopUID = -1;
this.stop.disabled = true;
this.start.disabled = false;

}

autoScrobbler = new AutoScrobbler();

This time, we’ve changed quite a lot. I’ll go through each of the changes.

Firstly, you’ll notice that each of the function headers (originally function addLatest() { and now AutoScrobbler.prototype.addLatest = function() {), have been changed. This is the best way of defining functions under the group that we grouped our variables under in the previous stage. There are other ways of doing this, but this generally has performance advantages. I’ve spoken about using prototype before, so if you’re interested in that, you should have a look at my post on using prototype.

Note (07/02/2013)

On reviewing the code for later articles, it has become clear that I have fallen into a classic “gotcha” or moving these functions into the same group as our variables.

Until I edited the code today, we had both a variable called start and a function called start(), when this code was run, the function (declared before the constructor function is called) is overwritten by the variable. So there is no start() function to call!

I have now corrected this, renaming the variables startElm and stopElm respectively. This is definitely something to check for if you are getting TypeErrors on running your code!

Secondly, you’ll notice that all references to autoScrobbler have been removed (except for one at the end). This is because everything is now in the same group of variables and functions, and we can use that third scope that we created in the previous stage without having to reference the name of the group explicitly, functions that are within the same group as variables can access these variables with the this keyword.

Next, you’ll notice that I’ve put all the code that was at the bottom at the top, and wrapped it in a function definition. This function header hasn’t been changed unlike the rest of the functions. This is part of the Object-Oriented style of coding. All this code was used to set up the variables, creating the user controls and essentially getting the program going. In Object-Oriented coding, this code goes in a constructor function (functions are often referred to as methods in Object-Oriented coding). Moving it to the top does nothing to the way in which the program runs, but is a more logical to have the code, because this is the code that runs to set up this group of variables and functions.

Finally, we’ve added a last line at the end, if you look at our previous stage you’ll see we have a similar line.

autoScrobbler = new Object();

In this stage we exchange the word Object for AutoScrobbler. In our previous stage, new Object() (as far as we need to know) set up our group to add variables, data and functions to. Using new AutoScrobbler() does the same setting up of the group, plus running any code in the constructor function.

A side effect of this is that we can create several of these groups, all of which can run at the same time and not interfere with each other. I said earlier that this probably isn’t something we want with our example, but it means that we could now, if we wanted, auto-scrobble two radio stations at a time.*

That’s it for this article, next we’ll be looking at extending, improving readability and describing code.

* Okay, not quite, if we tried this, the user controls would go a bit screwy, but the Javascript would not, the two would run independently of each other without an issue!

Advertisements

Refactoring Code in to an Object-Oriented Paradigm #2: Objects

This is article number 2 of a series of article, which simply documents my refactoring of some code I originally wrote (pretty poorly) last year. To get the gist of what’s going on. Check the original post – Refactoring Code in to an Object-Oriented Paradigm.

Using Objects

Javascript (and many other languages) has a variable type which, at it’s very simplest, will group other variables, data, even functions together. Usually these should be related to each other. We’ll start by grouping all of those global variables together, and for any variables which don’t need any greater scope than the local scope of that function, we’ll make sure they’re all defined with the var keyword. (I’ve stripped out the top comment for now)

function addLatest() {

var tracks = document.querySelectorAll(".userSong");
autoScrobbler.lastTrackUID = (typeof autoScrobbler.lastTrackUID == "undefined") ? tracks[1].querySelector("input").value : autoScrobbler.lastTrackUID;
for (var i = 0; i < tracks.length; i++) {

var item = tracks[i];
if (item.querySelector("input").value == autoScrobbler.lastTrackUID) {

i = tracks.length;
autoScrobbler.lastTrackUID = tracks[0].querySelector("input").value;

} else {

item.querySelector("input").checked = true;
autoScrobbler.scrobbled++;

}

}
doUserScrobble();
autoScrobbler.countReport.innerHTML = autoScrobbler.scrobbled;

}


function loadThenAdd() {

doRadioSearch();
setTimeout(addLatest, 30000);

}


function start() {

loadThenAdd();
autoScrobbler.loopUID = setInterval(loadThenAdd, 300000);
autoScrobbler.start.disabled = true;
autoScrobbler.stop.disabled = false;

}


function stop() {

clearInterval(autoScrobbler.loopUID);
autoScrobbler.lastTrackUID = undefined;
autoScrobbler.loopUID = -1;
autoScrobbler.stop.disabled = true;
autoScrobbler.start.disabled = false;

}


document.querySelector("#disclaimersContainer").innerHTML += "<div id=\"autoScrobbler\" style=\"background: #FFFFFF; border-top: 1px solid #000000; border-left: 1px solid #000000; position: fixed; bottom: 0; height: 50px; width: inherit;\"><input id=\"autoScrobblerStart\" type=\"button\" value=\"Start auto-scrobbling\" onclick=\"start();\" /> | <input id=\"autoScrobblerStop\" type=\"button\" value=\"Stop auto-scrobbling\" onclick=\"stop();\" /><p><span id=\"autoScrobblerScrobbleCount\">0</span> tracks scrobbled</p></div>";
autoScrobbler = new Object();
autoScrobbler.start = document.getElementById("autoScrobblerStart");
autoScrobbler.stop = document.getElementById("autoScrobblerStop");
autoScrobbler.loopUID = -1;
autoScrobbler.lastTrackUID = undefined;
autoScrobbler.scrobbled = 0;
autoScrobbler.countReport = document.getElementById("autoScrobblerTracksScrobbled");
start();

What we have now done, is grouped all the variables that we need to make the plugin work correctly, under one group, called autoScrobbler. This hasn’t completely solved the problem, all these variables are still accessible globally, however if someone else wants to use the global variable start, the variable that we’re using here won’t be affected, because it’s autoScrobbler object which is variable, so you have to reference autoScrobbler to access and of the variables within it.

If you think about this more, we have created a third scope, now we have our global scope (addLatest() references the doUserScrobble() function for instance, which is accessible globally), we have all the local scope (addLatest() references tracks, which is only accessible within that function, nothing else can access it) and we also have a selection of variables which can only be accessed if you reference autoScrobbler.[variable name]. This is our third scope, and you can also place functions into this group…

That’s it for this article, next we’ll be looking at fully Moving to Object-Oriented code patterns.

Refactoring Code in to an Object-Oriented Paradigm

A lot of people will probably know how to refactor code, and many probably know about Object-Oriented programming too. But this doesn’t apply to everyone, and some people may not even know where to start with refactoring code, even if they do understand the concept of OO.

In any case, I decided that I’d document my refactoring of some code I’ve previously written, partly so I could be a lot more meticulous about it (exposing your code to criticism makes you far more pedantic about each line), but also to maybe help someone struggling with the two ideas, or some of the issues that I currently have with my code.

I’m splitting it up into a series of articles, so that I can focus on each point of the refactoring process and make it clear what I’m doing and why I’m doing it! So I’ll start with the introduction, and the first stage of the process: introducing objects!

I’m using some Javascript code here, but much the article could be applied to refactoring code in any programming language.

The Current Code

The code I’m editing is the code used by the Auto-Scrobbler, here’s the source code we’re starting with:


/*
Auto Scrobbler for Universal Scrobbler
This needs to be cleaned up, the global variables and procedural
style is not perfect!
*/


function addLatest() {

tracks = document.querySelectorAll(".userSong");
lastValue = (typeof lastValue == "undefined") ? tracks[1].querySelector("input").value : lastValue;
for (var i = 0; i < tracks.length; i++) {

var item = tracks[i];
if (item.querySelector("input").value == lastValue) {

i = tracks.length;
lastValue = tracks[0].querySelector("input").value;

} else {

item.querySelector("input").checked = true;
autoScrobblerScrobbled++;

}

}
doUserScrobble();
autoScrobblerScrobbledElm.innerHTML = autoScrobbler.scrobbled;

}


function loadThenAdd() {

doRadioSearch();
setTimeout(addLatest, 30000);

}


function start() {

loadThenAdd();
document.autoScrobbleUID = setInterval(loadThenAdd, 300000);
autoScrobblerStart.disabled = true;
autoScrobblerStop.disabled = false;

}


function stop() {

clearInterval(document.autoScrobbleUID);
lastValue = undefined;
document.autoScrobbleUID = 0;
autoScrobblerStop.disabled = true;
autoScrobblerStart.disabled = false;

}


document.querySelector("#disclaimersContainer").innerHTML += "<div id=\"autoScrobbler\" style=\"background: #FFFFFF; border-top: 1px solid #000000; border-left: 1px solid #000000; position: fixed; bottom: 0; height: 50px; width: inherit;\"><input id=\"autoScrobblerStart\" type=\"button\" value=\"Start auto-scrobbling\" onclick=\"start();\" /> | <input id=\"autoScrobblerStop\" type=\"button\" value=\"Stop auto-scrobbling\" onclick=\"stop();\" /></div>";
autoScrobblerStart = document.getElementById("autoScrobblerStart");
autoScrobblerStop: = document.getElementById("autoScrobblerStop");
autoScrobblerScrobbled = 0;
autoScrobblerScrobbledElm = document.getElementById("autoScrobblerTracksScrobbled");
start();

Some Issues

Global Variables

The only way we can pass round data is by setting up half a dozen global variables. Privacy isn’t really an issue in Javascript, you can access anything, there’s no privacy (without a lot of hacking an memory hogging). But it’s more of an issue of efficiency, all the variables will fill up the browser memory, making the code pretty inefficient.

If nothing else, it just fills up the list of global variables, just for your piece of code. Which means other developers will have to work around your big mass of variables.

To add to this, I’ve done some weird tomfoolery where I wanted to ensure that I was setting a global variable, by inserting variables into the “document” variable, which usually holds all the elements in the current document. If anything this should’ve been done in the “window” variable, where global variables are set by default. This would mean I wouldn’t be playing about with document properties, which is very bad practise!

No Chance of Multiple Instances

It doesn’t apply to this piece of code so much, but you can’t run multiple instances of this code. If I wanted to automatically scrobble two radio stations simultaneously (though I’m not sure why I’d want to), I wouldn’t be able to in the code’s current state. This would be more of an issue for something like an element fading script. If it was written in the same way as the code above, you’d only be able to fade one element on the page – that’s not very useful!

Difficult to extend, especially for others!

Because of the way that the code is written, extra functionality is hard to implement, old methods need to be rewritten (sometimes completely) to add any sort of functionality. It’d be nice to fix this, even allow other plugins to work with mine.

The way it’s written isn’t the most intuitive either, there’s no real flow, and if you want to follow the code as a developer who’s never seen the code before, you’ll be getting RSI from scrolling up and down if the code gets any longer! What’s worse is that there’s just one comment, the rest of the code has no information about what each function does.

It uses inline CSS and inline event handling

The code currently  injects some HTML into the page, to give the user some control over the automatic scrobbling. I wrote the whole plugin in the Chrome console, and at the time, both inline CSS and inline event handling seemed like the quickest way to write things. However, in terms of best practise, unobtrusive Javascript and CSS efficiency, it isn’t a good option to write any styles or event handles inline.

Limited Compatibility

It would be lovely to run one function or line of code, and expect it to work with every browser on every platform. Unfortunately, that’s just not how things are. Older browsers had a far more primitive method of handling events, and IE chose it’s own special way of doing things as well (as usual). Further to that, the W3C standard is also entirely different, so we need three different lines of code just to do one thing, this is also true of other functions I use in my code. As part of the refactor, it’d be great to get this compatibility for more browsers.

Let’s fix this

Those issues aren’t nice to have in production code, they may have been fine when I was trying to whip up something as quick as possible, but I’ve now drawn a line under it, and revealed it to others. So the code needs to be refactored. Refactoring won’t add any functionality to the plugin, nor will it change the resulting product or the data that is passed through the code.

In fact, perfect code refactoring would leave an end-user with the exact same functional experience as they had before. The difference is in how many resources are required to run the program, how easy it will be to add to that program later and how well other developers will be able to understand the code if they should look at it.

We’ll start with those global variables, we still want to be able to access them in any scope…

Aside: Scope

“A scope is the context in which a variable name or other identifier is valid and can be used.”

I’ve been meaning to write an article on the scope of variables and the problems I’ve been tripped up by just by refactoring code poorly, unfortunately I haven’t been able to do that as of yet!

You’ll need an idea of scope for this series of articles to make any sense at all, so here’s why we need to deal with different scopes in our specific example:

  • In it’s current state, there are two scopes:
    • Variables which are first set with the var keyword have a local scope. This means that, for instance, in the addLatest() function, the variable item may only be accessed within that function. If the loadThenAdd() function referenced the variable “item“, an error would be thrown, as it would not exist (because loadThenAdd() is outside of addLatest() scope). 
    • Variables which are first set without the var keyword have a global scope. This means that the variable could be referenced and used anywhere. So the start() function can set a value to document.autoScrobbleUID, and the stop() function can use that value to stop the auto-scrobbling of tracks.
    • Unless you know that you want to be using data between several functions, or that you want to retain data after the function has finished running, you should always put variables in a local scope. If you don’t, you can find some very tricky problems will occur (suddenly, you’ll find yourself overwriting variables in other parts of your code and other parts never running because the conditions aren’t right). I have in fact broken this rule in addLatest(), where tracks is used only by addLatest() and it doesn’t need to be retained after the function finishes, but it isn’t defined/set using the var keyword before it.
  • We use global variables for a lot of the variables in this code, because they are  required by multiple functions.
  • We also use the setInterval() function in the code, and any code you run within it has it’s own scope (it wouldn’t be able to access any local variables in the start() function for instance), so this also requires something with more access than local variables to do it’s job.
  • Understanding scope can help to make your programs more efficient which will increase performance as well!
  • The refactoring of our code will add a third scope into the mix, which solves many of these problems.

So we need to offer these variables globally (or at least in a wider scope than the local scope of each function), without cluttering up the rest of the page.

I am now going to refactor the code in five stages, I will be writing an article dedicated to each.

Objects

Javascript (and many other languages) has a variable type which, at it’s very simplest, will group other variables, data, even functions together… Read how I introduced objects into the code

Moving to Object-Oriented code patterns

So now we have our group of variables under the name autoScrobbler, I said earlier that we could also include functions under this group… Read how I moved my code to a more Object-Oriented pattern

Extendible, Readable, Descriptive

We’re nearly there with the Javascript, the worst has been cleaned up generally. Now we’re getting on to being developer-friendly, which is pretty important, you want people to extend your program… Read how I begin making my code more developer friendly.

Cleaning Up Our Messy User Controls

We’re pretty close to being done here in terms of refactoring our Javascript. But the Auto-Scrobbler has a UI, an incredibly simple UI, but it still has one… Read how I clean up the messy UI code.

Compatibility

Javascript: Using Prototype

It seems that I have missed a major part of Javascript, and even more importantly, I’ve missed one of the fundamental attributes and styles of Javascript! From what I’m seeing round the Internet, I’m definitely not the only one, in fact it seems as if even some seasoned professionals, who’ve worked with Javascript with years, still don’t use or quite understand the concept of prototyping.

I myself am completely new to the concept. I’ve seen code with prototyping in before, but I’ve not known anything about it, and I’ve found other ways around it. If you’ve come across it before, maybe in an older implementation of Javascript, and need your Prototype education rewiring, you should maybe look at the article by Angus Croll (which I’ve also linked in the Further Reading section at the bottom). Otherwise, I’ll see if I can explain it.

You can go without using prototypes directly for as long as you use Javascript, but chances are that you’ll be using prototypes in everything you do and if you’re using a library like jQuery then there’s no question that you’ll be using prototypes. Many core Javascript objects use prototypes already, if you’ve ever use split() or replace() on a string – you’ll have used a prototype. But prototyping is not essential to programming in Javascript, which is why many go without using or knowing about it, however, it is the key to huge performance gains. Something which is very important if you’re making larger, more interactive pages with a lot of Javascript code.

An Example

Finding an example of this which could affect almost all Javascript developers was actually quite hard, as it doesn’t affect many in a massive way until you get into the aforementioned more interactive, larger pages. But I think I’ve found a scenario, sadly I’ve still had to explain a different style of coding, which really could go in another blog, but it’s nevertheless useful to explain objects and their place in programming.

Using simple functions

Say you wanted a set of functions which would change some text in a paragraph tag (<p>), to keep things simple, I only want to change what the actual text says and the colour of the text. Most people would instantly go for the following approach.

function changeText(pTagName, toWhat) {
document.getElementById(pTagName).innerHTML = toWhat;
  return true;
}

function changeColour(pTag, toWhat) {
  //do functions for colour change
  alert("colour changed to "+toWhat);
  return true;
}

And to the value of two p tags, you’d do the following.

changeText("pTag1","This is the new text.");
changeText("pTag2","This is the new text.");

But what if you had 10 things you wanted to be able to do to the paragraph tag? Or 100, what if you wanted to expand it to changing other elements? Really you’d want to have one function that did it all, right?

Using Objects

You can do this by using Objects. With an object you can write methods, which (in this case) will change anything you want about the paragraph tag. In this example, that’ll look like this.

function TextChanger(pTag) {
  this.textObj = document.getElementById(pTag);

  this.changeColour = function(toWhat) {
    //do functions for colour change
    alert("colour changed to "+toWhat);
    return true;
  };

  this.changeWords = function(toWhat) {
    this.textObj.innerHTML = toWhat;
    return true;
  };
}

We can then create an object which will change just the attributes of one paragraph tag each, making it much quicker and easier to manipulate it later on in your script. Now, we’d change the text like this.

//Assign a TextChanger object to both paragraph tags.
var text1 = new TextChanger("pTag1");
var text2 = new TextChanger("pTag2");
text1.changeWords("This was an object method changing me, the first one!");
text2.changeWords("This was an object method changing me, the second one!");

//Aside: If we wanted to change the colour now, we can do it like this.
text1.changeColour("blue");
text2.changeColour("pink");

You could stop there, that will still be faster than the first approach, and now everything will be contained, so things will still be easier to work with. But you can still get faster performance just from this simple example!

Using Prototypes with your Object

The problem is that every time you create a new TextChanger() object, it has to also create an instance of the changeWords() and the changeColour() method. And that can build up a surprising amount, especially if you’re using objects to do something a lot more complex or resource intensive.

So now we’re finally at the point where Prototypes come in. With a prototype, you can declare a function which will be available to every single instance of that TextChanger() object, or whatever object you want. Just about anything that is a function or a value in Javascript can have a prototype. Integers, Strings (more on this later), pre-existing Javascript objects, your own objects, whatever you want.

In our example here, if you use prototypes, it means that Javascript only needs to create one instance of changeWords() and one instance of changeColour() for any instance of TextChanger, whether you have one instance or a million instances. And as before, that really does start affecting performance once you start doing more complex operations!

To use prototypes in our code, we can do the following.

function TextChanger(pTag) {
  this.textObj = document.getElementById(pTag);
}

TextChanger.prototype.changeColour = function(toWhat) {
  //do functions for colour change
  alert("colour changed to "+toWhat);
  return true;
};
TextChanger.prototype.changeWords = function(toWhat) {
  this.textObj.innerHTML = toWhat;
  return true;
};

It’s pretty similar to what we saw in the above example with just declaring an Object. The only difference is that you declare your Object function and then outside of that function, you can declare your methods with “TextChanger.prototype.changeWords =” and your function.

To use it, it’s the same as using an object method (as shown above).

var text1 = new TextChanger("pTag1");
var text2 = new TextChanger("pTag2");
text1.changeWords("This was an object method changing me, the first one!");
text2.changeWords("This was an object method changing me, the second one!");

And to see the results of going through all that change? I’ve set up a benchmark test on jsPerf (a javascript benchmarking site that I would highly recommend!) and you can find that at http://jsperf.com/change-text-test. It differs slightly to the examples shown here, though only marginally. We’re still doing the same operations to get the same result.

In the case of the simple function test, the element has be retrieved each time and then it’s operated on. In the case of the Objects using nested methods, it’s actually slower, in this test, than the simple function! This is because it’s having to retrieve the element the same number of times as the simple function test and it’s having to create several instances of both methods. If you used this approach and did more using it, you’d find that actually, it would come out on top of the simple function test.

But it’s the Object with prototyped test which really shows the improvement on both these methods, it still has to retrieve the element for each paragraph tag, but only one instance of each method is created, and the actual operation takes less time because the element is already stored as an object property.

A time saver using prototypes

Because everything can have a prototype in Javascript, it means you can add some very interesting functionality into core features of Javascript. For instance, lets make a method to add the html entity for a bullet point to any string, using prototypes.

String.prototype.bullet = function() {
 return "&bull; "+this;
}

"Item 1".bullet(); //Returns "&bull; Item 1" or "• Item 1" in HTML

That’s pretty useful, but there are so many more useful things that can be done with prototypes. Explore what there is out there!

Any notes?

It should be noted that if you’re using a Javascript library (like jQuery), then you may come in to some problems with prototyping. Just Googling “prototype and jQuery” will show just a sample of these problems. So just be cautious if you do decide to use both of these together.

Just a quick aside!

Completely unrelated, I’d just like to once again recommend you try Adobe Photoshop CS6 while it’s still running in public beta, I made the banner for this blog post using just CS6 and it just keeps offering more and more. I really am impressed!

Further Reading

 jsPerf Benchmark test using the examples seen here
 General information about optimising your code from Nettuts
 Understanding Javascript Prototypes from the Javascript, Javascript blog
 Dmitry A. Soshnikov’s article on prototypes