5

Fair warning - a long time ago I wrote a lot of C++ and can't help the temptation to coerce javascript into design patterns I was familiar with back then. It's ok to accuse me of atavism in any replies ;-)


In my current project, I want to create objects by name, which indicates the factory pattern. So I read the top page of google hits for 'javascript factory pattern'. They all have this ugly thing in common:

if (name === 'FactoryPartA') {
    parentClass = PartA;
} else if (name === 'FactoryPartB') {
    parentClass = PartB;
} else if ...
    parentClass = PartZ;
}

return new parentClass();

Which has 2 problems:

  1. Every time I create a new part for the factory to make, I have to edit the factory's implementation and I'd prefer to avoid both the work & the bug-insertion opportunity.
  2. It's a hard-coded, linear search which hardly screams "efficiency!"

So here's what I came up with - a combination of the module and factory patterns with the info hiding merits of the module pattern provided by the tactic of defining the classes of factory parts inside the factory's register closure.

Finally getting to my question: I can't believe that this hasn't been done before by better coders than me so please share a link to the canonical version of this twist on the factory pattern if you know of one.

N.B. For this example I've run all my code together. In my project the factory, FactoryPartA, FactoryPartB, and client code are all in separate files.

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = [];

    // register an item
    api.register = function (item) {
        if (registry.some (function (r) {return r.name === item.name;})) { 
            throw new Error ('factory.register(): name collision detected: ' + name);
        } else {
            registry.push(item);
        }
    };

    // make an item given its name
    api.make = function (name) {
        var item = null;
        var idx = registry.findIndex (function (r) {
            return r.name === name;
        });
        if (idx >= 0) {
            item = new registry[idx].make();
        }
        return item;
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register ({
    name: 'FactoryPartA',
    make: function FactoryPartA () {
        'use strict';
        var label = 'Factory Part A';   // private property

        this.test = undefined;  // public property

        this.label = function () {   // public method
            return label;
        };

        return this;
    }
});

// define a different module & register it with factory
mynamespace.factory.register ({
    name: 'FactoryPartB',
    make: function FactoryPartB () {
        'use strict';
        var label = 'Factory Part B';

        this.test = undefined;

        this.label = function () {
            return label;
        };

        return this;
    }
});

// client code
var aPart = mynamespace.factory.make('FactoryPartA');
var bPart = mynamespace.factory.make('FactoryPartB');

console.log (aPart.label()); // logs 'Factory Part A'
console.log (bPart.label()); // logs 'Factory Part B'

var anotherPart = mynamespace.factory.make('FactoryPartA');
aPart.test = 'this one is not';
anotherPart.test = 'the same as this one';
console.log (aPart.test !== anotherPart.test); // logs true
1
  • 4
    This kind of dynamic factory linking is exactly the problem dependency injection was created to solve (shameless link to my own library). Commented Apr 29, 2016 at 17:06

1 Answer 1

6

To answer the fundamental question - is this a new Javascript factory pattern - no, it is not. (Check out ES6/ES2015, Typescript, and Aurelia's dependency injection module.)

Now, to answer the overall statement - what you're essentially doing is trying to add metadata to a "class" type in Javascript. The thing is, it looks like you're trying to create a factory of factories - which I'm not sure you need. (Perhaps you've simplified your example.)

With your example, I'd do something more like this:

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = {};

    // register an item
    api.register = function (name, item, overwrite) {
        if (!overwrite || registry.hasOwnProperty('name')) { 
            throw new Error ('factory.register(): name collision detected: ' + name);
        }

        registry[name] = item;
    };

    // make an item given its name
    api.make = function (name) {
        var item = registry[name];
        return item ? new item() : null; // or better, Object.create(item);
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register ('FactoryPartA', function FactoryPartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
    };
});

// define a different module & register it with factory
mynamespace.factory.register ('FactoryPartB', function FactoryPartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
});

This removes the extra factory stuff so that it's not a FactoryFactory. (Not sure why you had that.) Further, you mentioned a linear search - by using a object instead of an array, you avoid a linear lookup (object hashes are constant-time lookup). Finally - you can actually register any name without having to place it into a wrapper object. This is closer to DI.

If you really want to do the metadata-style approach, though, you can do something like the following:

namespace('mynamespace');

// object factory
mynamespace.factory = (function () {
    'use strict';
    var api = {};
    var registry = {};

    // register an item
    api.register = function (item, overwrite) {
        if (!overwrite || registry.hasOwnProperty(item.name)) { 
            throw new Error ('factory.register(): name collision detected: ' + item.name);
        }

        registry[item.name] = item;
    };

    // make an item given its name
    api.make = function (name) {
        var item = registry[name];
        return item ? new item() : null; // or better, Object.create(item);
    };

    return api;

})();


// define a module & register it with factory
mynamespace.factory.register (function FactoryPartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
    };
});

// define a different module & register it with factory
mynamespace.factory.register (function FactoryPartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
});

This uses the function name instead of a separate property. (Functions can be named or anonymous in Javascript. This method would not work with anonymous functions.) I mentioned Typescript above because when Typescript compiles to Javascript, it actually places a lot of its own metadata on objects, similar to what you're doing. I mentioned Aurelia's dependency injection because its @autoinject functionality actually reads this metadata to create objects, similar to what you're doing.

But really....unless you're attempting to create a dependency injection container (which would require more logic than is in your example - you'd want a container with keys that can point to instances as well) I would argue that you'll get much more functionality out of Object.create() and Object.assign() than you would with this pattern. A lot of the design patterns that you need in a statically typed, strongly typed, compiled language, aren't necessary outside of that environment. So you can do this:

function PartA () {
    'use strict';
    var label = 'Factory Part A';   // private property

    this.test = undefined;  // public property

    this.label = function () {   // public method
        return label;
}

function PartB () {
    'use strict';
    var label = 'Factory Part B';

    this.test = undefined;

    this.label = function () {
        return label;
    };
}

var A = Object.create(PartA);
var B = Object.create(PartB);

This is, simply, much easier.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for such a comprehensive and illuminating answer. Knowing that hashes are constant time is really useful.
I hope that the rest was useful as well. This isn't necessarily the best answer, as I've skimmed (and skipped) over a lot of details that are relevant to the question and the answer. Most of the design patterns we use are tailored to coerce the type system to our wishes - they are also, incidentally, mainly designed for classical OOP. Further, some of these design patterns aren't needed in higher-level languages. I would highly suggest reading some documentation on JavaScript - you can use its high-level features to assist you, and use its functional side to avoid boilerplate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.