Imagine you inherited a legacy code that calculates and displays a new annual balance for an investment given a fixed rate and an initial amount:
//global variables
var balance = 1500,
interest_rate = 4.3;
//calculate new balance per year by adding the new interest from the old balance
function calculateAnnualBalance(){
balance = balance * (1 + (interest_rate/100));
}
function getBalance(n){
var label = "Balance On Year " + n + " is $";
balance = label + balance;
return balance;
}
function printBalance(){
calculateNewBalance(); //increment balance
console.log(getBalance(1));
}
printBalance(); // will output "Balance On Year 1 is $1564.5";
Running the code on the console will output like this:
Life is great and wonderful until a new requirement comes in. The new requirement is to support multiple years of balances and print them individually.
To support the new requirement, your new co-worker modified the printBalance
function like this:
.... more code before
function printBalance(years){
for (var i = 1; i <= years; i++){
calculateNewBalance(); //increment balance
console.log(getBalance(i));
}
}
printBalance(10); //print all balances in 10 years
Running the code, the output will now contain a lot of “NaN” values.
This is because of the balance
variable being exposed globally.
The getBalance
function has a direct access to the balance
value
consequently changing the variable’s type from number to string.
This is just a trivial example and we can fix the bug easily. However, imagine this in a real world application and there are tons of spaghetti code in here. Also to make the matter worst, what if the new co-worker went home without telling you the problem. I think you got my point here.
One solid and long term solution is to modularize the code. Modularly structuring a code can protect data integrity. To illustrate, let’s rewrite the code above.
var Balance = (function(){
//these are now private variables thus cannot be accessed outside the module
var balance = 1500,
interest_rate = 4.3;
function calculateNewBalance(){
balance = balance * (1 + (interest_rate/100));
}
function getBalance(){
return balance;
}
//expose the functions outside the module
return {
calculateNewBalance: calculateNewBalance,
getBalance: getBalance
}
})();
We start by declaring a new Balance
module.
The functions and variables are the same except we added a new getBalance
function.
Take note that getBalance
function does not modify the balance
value but only gets it.
Also balance
variable isn’t accessible outside the module assuring us that the data type for this variable
isn’t modified anywhere else.
Given that there’s a new developer on the team, the only way he/she can access the balance
variable outside the module
is to call the getBalance
function.
Next is to create a submodule for displaying output.
Balance.displayUtility = (function(){
function getBalance(n){
var label = "Balance On Year " + n + " is $",
balance = label + Balance.getBalance(); //call the getBalance function from the main module
return balance;
}
function printBalance(years){
for (var i = 1; i <= years; i++){
Balance.calculateNewBalance(); //increment balance using the module's function
console.log(getBalance(i));
}
}
return {
printBalance: printBalance
}
})();
A new submodule for Balance
called displayUtility is created.
Since balance
isn’t global anymore, we call the main module’s getBalance
function instead on this line.
...more code
var label = "Balance On Year " + n + " is $",
balance = label + Balance.getBalance();
... more code
To print the balances in 10 years, we call the printBalance
function w/c calls the getBalance
function as well:
Balance.displayUtility.printBalance(10);
The console’s output as expected is now:
Here’s our whole Balance
module:
var Balance = (function(){
var balance = 1500,
interest_rate = 4.3;
function calculateNewBalance(){
balance = balance * (1 + (interest_rate/100));
}
function getBalance(){
return balance;
}
return {
calculateNewBalance: calculateNewBalance,
getBalance: getBalance
}
})();
Balance.displayUtility = (function(){
//private function
function getBalance(n){
var label = "Balance On Year " + n + " is $",
balance = label + Balance.getBalance();
return balance;
}
function printBalance(years){
for (var i = 1; i <= years; i++){
Balance.calculateNewBalance(); //increment balance
console.log(getBalance(i));
}
}
return {
printBalance: printBalance
}
})();
Balance.displayUtility.printBalance(10);