Shaping Your Program

“A beginning programmer writes her programs like an ant builds her hill, one piece at a time, without thought for the bigger structure. Her programs will be like loose sand. They may stand for a while, but growing too big they fall apart.

Realizing this problem, the programmer will start to spend a lot of time thinking about structure. Her programs will be rigidly structured, like rock sculptures. They are solid, but when they must change, violence must be done to them.

The master programmer knows when to apply structure and when to leave things in their simple form. Her programs are like clay, solid yet malleable.”

-- Master Yuan-Ma, The Book of Programming

Eloquent JavaScript, Chapter 10: Modules

What Are Modules?

Modules are for shaping your program and organizing code. A module is a group of values and functions that work closely together. Each module implements a different functionality of the program.

Why Use Modules?
  • Help to decompose the program into more manageable chunks and organize the code.
  • More contained failure -- Decoupling (if one module breaks, another will continue to work--unless it depends on the broken module)
  • Reusability

This will make it much easier to maintain in the future. Newer developers often are unsure what it means to maintain code, so let's consider an example.

True Story: Why Make Extra Effort to Produce High-Quality Code

A developer built an application for a client after she had first started working as a junior dev. She knew she had a learning curve to overcome, and she wanted to do things in the best way possible, but she had deadlines to meet in order to please the client. The client didn't know whether her code followed best practices or not, nor did they have any idea how new she was. All they could see was that she was getting the work done and that it worked in an acceptable manner.

Fast-forward 1 year, and the junior dev is now a solid developer. She has completed many projects and learned to program efficiently and in a way that reduces the effort needed to maintain that code. She is content with her job and enjoys working with the client in order to make great tech, but then she suddenly is asked to add more functionality to her very first project. She goes back to look at the code and remembers all the lessons that project taught her, and how difficult coding had seemed back then. As she tries to implement new functions in the code she realizes that making any changes to the code causes errors in the program. She also realizes that this old code depended on deprecated functions that were no longer usable anymore and she'd have to figure out a better way to do work she'd already done a year ago.

She read through her code jumping between dozens of different files some containing functions with over 200 lines of code. She found that those were the most difficult functions to understand because they were just too complex. It is always best to have functions do one thing and do it well, but she didn't understand that back when she had first written that code.

From that day onward she vowed to never again write code that was hard to be maintained, it wasted so much time and money to re-factor it, but after she did she began to enjoy her job again.

Reusing modules

If something already exists in the form you need it, it's best practice to not waste your time and other people's time by re-inventing the wheel. Instead one should look to see if that functionality could be done with a light-weight and fully-functional module that already exists. One service that will help with that is Node Package Manager (where readline-sync comes from).

Go to the website with all the packages you can search.

Creating a Module

Create a file called greeting.js that contains the following code:

module.exports = "Hello V School"

Create another file called main.js that contains the following code:

var myGreeting = require('./greeting.js');
// or
var myGreeting = require('./greeting');

console.log(myGreeting);
Module.exports

The only part of the module that is accessible to other parts of the program is module.exports. Whatever is bound to module.exports is returned from the ‘require’ expression.

Modules Only Run Once

Create a file called once.js that contains the following code:

console.log('This will only print once');

Create another file called twice.js that contains the following code:

require('./once');
require('./once');
// will only run the module once
Namespacing

Create a file called namespacing.js that contains the following code:

var private = "No one else can see this";
var public = "Everyone can see this";

module.exports = public;

Create another file called main.js that contains the following code:

var namespacing = require('./namespacing');

console.log(namespacing); // Everyone can see this
console.log(private); // error
Exporting functions

Create a file called string-reverse.js that contains the following code:

var reverse = function (string) {
 return string.split('').reverse().join('');
};

module.exports = reverse;

Create another file called main.js that contains the following code:

var reverse = require('./string-reverse');

console.log(reverse); // function definition
console.log(reverse('apple')); // elppa
Exporting objects

Create a file called villain.js: that contains the following code:

module.exports = {
 name: 'The Joker',
 type: 'evil',
 enemy: 'Batman',
 speak: function () {
   console.log('My name is ' + this.name);
   console.log('I am ' + this.type + ' and my enemy is ' + this.enemy);
 }
};

Create another file called main.js that contains the following code:

var villain = require('./villain');
villain.speak();
Module-Objects as Function Libraries

It can be extremely useful to categorize your functions into groups and store them in separate places so that they can be reusable and easily found. A good type of module to have would be a Utilities library, that had useful programming helper-functions that could be used anywhere in your code. Here are some ideas of things that would be best put in a module:

  • Utility functions
  • Validation functions
  • Functions related to a particular task or class
  • HTTP functions
  • Parsing functions
  • Formatter functions
  • Masking functions
  • Search functions

Create a file called library.js: that contains the following code:

var functions = {
	reverse: function (string) {
	 return string.split('').reverse().join('');
	},

	reverseTwice: function (string) {
	 string = string.split('').reverse().join('');
	 return string.split('').reverse().join('');
	}
}

module.exports = functions;

Create another file called main.js that contains the following code:

var reverseFunctions = require('./library');

// Reverse once
console.log(reverseFunctions.reverse('apple')); // elppa

// Reverse twice
console.log(reverseFunctions.reverseTwice('apple')); // apple

console.log(reverseFunctions.reverseTwice.toString()); // a function in the imported module.