NodeJS Module Factories

A factory is an object that creates and returns other objects. It is often used to hide complex implementation details. For example returning a new database connection and taking care of the parameters behind the scenes. With NodeJS a factory can also help deal with cache issues when declaring several instances of a module.

In a typical programming language you might see something like the example below. Two instance of a class being instantiated, returning two distinct objects:

Counter A = new Counter();
Counter B = new Counter();

for( var i = 0; i < 5;  i++ ) {
    log( A.step() );    // Should equal 1, then 2, 3, 4, 5
    log( B.step() );    // Should equal 1, then 2, 3, 4, 5
}

When translating code like that to NodeJS, you might think that you can just do something like this:

var counterA = require("./counter");
var counterB = require("./counter");

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counter A: %s, Counter B: %s", 
        counterA.step(), 
        counterB.step());
}

You may expect a result like this:

Counter A: 1, Counter B: 1
Counter A: 2, Counter B: 2
Counter A: 3, Counter B: 3
Counter A: 4, Counter B: 4
Counter A: 5, Counter B: 5

But instead, you get this:

Counter A: 1, Counter B: 2
Counter A: 3, Counter B: 4
Counter A: 5, Counter B: 6
Counter A: 7, Counter B: 8
Counter A: 9, Counter B: 10

The problem is that the first require call caches the object and then the second call just points to that first object. You could try using the delete command against the cache. But that’s not a very good way to handle it. The better option is to use a factory.

Now I’ll walk you through a demo showing the problem and how to fix it with a factory.

Create a Counter Module

Create a new project folder and using your favorite text editor create a file called counter.js:

counter.js

module.exports = {
    _counter: 1,
    step: function() {
        return this._counter++;
    }
};

This will return an object that has one method: step. When step() is called it increments an internal counter (_counter) and returns the updated value.

Test the Counter Module

Create a another file to test using one instance of the module. Name the file test-one.js:

listing: test-one.js

var counterA = require("./counter");

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counter A: %s", 
        counterA.step());
}

Run the test:

$ node test-one.js

Here is what you get for a result.

Counter A: 1
Counter A: 2
Counter A: 3
Counter A: 4
Counter A: 5

It looks good. This is what we want.


Test Two Instances

Create another file to test using two instances of the module.  Call the file test-two.js:

listing: test-two.js

var counterA = require("./counter");
var counterB = require("./counter");

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counter A: %s, Counter B: %s", 
        counterA.step(), 
        counterB.step());
}

Run the test:

$ node test-two.js

Here is the result. But it’s not good.

Counter A: 1, Counter B: 2
Counter A: 3, Counter B: 4
Counter A: 5, Counter B: 6
Counter A: 7, Counter B: 8
Counter A: 9, Counter B: 10

With each call to step() on either counter both counters are being incremented!

The expected result should have been this:

Counter A: 1, Counter B: 1
Counter A: 2, Counter B: 2
Counter A: 3, Counter B: 3
Counter A: 4, Counter B: 4
Counter A: 5, Counter B: 5

A Bad Example of a Factory

The suggestion is usually to create an independent factory module that returns the original object defined in another module. But in the case of our counter that strategy won’t work. Here is a demo of why.

Create a new file called bad-factory.js:

listing: bad-counter-factory.js

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

module.exports.create = function() {
    return counter;
};

Create another file to test it called test-bad-factory.js:

listing: test-bad-factory.js

var factory = require("./bad-factory");

var counterA = factory.create();
var counterB = factory.create();

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counter A: %s, Counter B: %s", 
        counterA.step(), 
        counterB.step());
}

Run the test:

$ node test-bad-factory.js

The result is still not good:

Counter A: 1, Counter B: 2
Counter A: 3, Counter B: 4
Counter A: 5, Counter B: 6
Counter A: 7, Counter B: 8
Counter A: 9, Counter B: 10

A Better Example of a Factory

One way to create an object from a factory is to return the object defined with the factory. That leaves require out of the picture so there are no caching issues.

Create a new file called counter-factory.js that incorporates the original counter code in the factory definition:

listing: counter-factory.js

module.exports.create = function() {
    var _counter = 1;
    return {
        step: function() {
            return _counter++;
        }
    }
};

Create a new file to test it called test-factory.js:

listing: test-factory.js

var factory = require("./counter-factory");

var counterA = factory.create();
var counterB = factory.create();

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counter A: %s, Counter B: %s", 
        counterA.step(), 
        counterB.step());
}

Run the test:

$ node test-factory.js

Success!

Counter A: 1, Counter B: 1
Counter A: 2, Counter B: 2
Counter A: 3, Counter B: 3
Counter A: 4, Counter B: 4
Counter A: 5, Counter B: 5

The two counters are no longer interfering with each other!


Test with Two Factories

You should never need two factories for one type of object. Even if you try caching will get in the way. But it’s always good to write a test to make sure that things won’t go wrong.

Create a new file to test with two factories. Call the file test-two-factories.js:

listing: test-two-factories.js

var factoryX = require("./counter-factory");
var factoryY = require("./counter-factory");

var counterA = factoryX.create();
var counterB = factoryX.create();
var counterC = factoryY.create();
var counterD = factoryY.create();

for( var i = 0; i < 5;  i++ ) {
    console.log( "Counters: %s, %s, %s, %s", 
        counterA.step(),
        counterB.step(), 
        counterC.step(),
        counterD.step());
}

Run the test:

$ node test-two-factories.js

Success!

Counters: 1, 1, 1, 1
Counters: 2, 2, 2, 2
Counters: 3, 3, 3, 3
Counters: 4, 4, 4, 4
Counters: 5, 5, 5, 5

No interference.

Source Code

The code used in this article can be found here:


Mitch Allen is a game developer and tech writer. To download his latest games and books please visit his Web site at mitchallen.com