3
\$\begingroup\$

I wanted to step up my JS game, and move on from mostly closure based scripting, so I decided to write an application in node. I don't have much experience with prototype based programming, and I guess it's a little too early for ES6 (well, maybe with traceur).

During planning of my app I saw that often I'll need container behaviour. So, I know I could implement it with a class, but I thought: why not use trait-like thingy instead?

Gamemode.js, concrete class

'use strict';

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

var Gamemode = module.exports = function () {
    Script.apply(this, arguments);
};

Gamemode.prototype = Object.create(Script.prototype);
Gamemode.prototype.constructor = Gamemode;

Script.js, abstract class

'use strict';

var helper = require("./helper");
var _ = require("lodash");

var Script = module.exports = function () {
    if (this.constructor === Script) {
        throw new Error("Can't initialize abstract class!");
    }

    var defaultDefinitions = {
        MAX_PLAYERS: '(500)'
    };

    _.merge(this, helper.collection("definition", defaultDefinitions));
};

Script.prototype.build = function () {
    return this.definitions;
};

helper.js

'use strict';

var _ = require("lodash");

var capitalize = function (string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
};

var container = function (singular, mutable) {
    if (undefined === mutable) {
        mutable = false;
    }

    var accessors = {},
        methodSingular = capitalize(singular),
        plural = singular + 's',
        methodPlural = capitalize(plural);

    accessors['get' + methodPlural] = function () {
        return this[plural];
    };

    accessors['set' + methodPlural] = function (value) {
        if (undefined === value) {
            delete this[plural]; 
        }

        this[plural] = value;

        return this;
    };


    if (mutable) {
        accessors['add' + methodSingular] = function (item) {
            _.assign(this[plural], item);
        };

        accessors['remove' + methodSingular] = function (item) {
            delete this[plural][item];
        };
    }

    return accessors;
};

var collection = function (name, defaults) {
    if (undefined === defaults) {
        defaults = {};
    };

    var all = {};
    all[name + 's'] = _.defaults({}, defaults);

    return _.merge(all, container(name, true));
};

var helper = module.exports = {
    capitalize: capitalize,
    container: container,
    collection: collection
};

It works properly, now every instance of Gamemode has its own "definitions" property, with getDefinitions, setDefinitions, addDefinition and removeDefinition methods.

  1. Am I violating any rules, like encapsulation?
  2. Am I adding property somewhere else than in the constructor?
  3. Would creating a "container" object instead be better?
\$\endgroup\$
2
\$\begingroup\$

Interesting question,

this code pass JSHint, it is readable, well organized and except for return definitions; (where did you declare definitions?) holds few mysteries to me.

Still, when I take a step back, why not simple use a bog standard Object? It seems as if you just re-invented the Object with few added benefits.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Oh, while stripping my code for this thread it seems I've forgot about this. Thanks for noticing! I created special object instead (that thingy), but you made me realise that I should rely on Object instead (my .all, .get and .set are really unncecessary). But, as there isn't Object.prototype.__nosuchmethod__ available, how can I achieve something like this with standard []/. accessor? \$\endgroup\$ – Misiur Dec 2 '14 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.