Will Roe's blog

Anaλysis Paraλysis » Archives

JavaScript has a liberal array of techniques for constructing objects. Although object oriented style programming can be simulated in Javascript (which is prototypical in nature), it can get a little unwieldy.

Pseudoclassical OO

The following code is in the style referred to as pseudoclassical by Douglas Crockford in Javascript: The Good Parts:

var Person = function(name, empathy) {
    this.name = name;
    this.empathy = empathy || 0;
};

Person.prototype.get_name = function() {
    return this.name;
};

Person.prototype.get_empathy = function() {
    return this.empathy;
};

Person.prototype.change_empathy = function(amount) {
    this.empathy = this.empathy + amount;
    return this.empathy;
};

Person.prototype.status = function() {
    return this.get_name() + " has " + this.get_empathy() + " empathy";
};

var Player = function(name, empathy) {
    this.name = name;
    this.empathy = empathy || 0;
};

Player.prototype = new Person();

var Mob = function(name, empathy) {
    this.name = name;
    this.empathy = empathy || 0;
};

Mob.prototype = new Person();

And we can use the ‘classes’ like so:

var player = new Player("Ada", 100);
console.log(player.status()); // 'Ada has 100 empathy'

var mob = new Mob("Bob", -10);
console.log(mob.status()); // 'Bob has -10 empathy'

mob.change_empathy(2);
console.log(mob.status()); // 'Bob has -8 empathy'

There are a few things to note here:

  • There are no private fields or methods, it’s not feasible to add them
  • There is duplication in constructors that lie in an inheritance chain (i.e. where an object’s prototype is set to another object)
  • It’s horribly verbose (a matter of personal taste however)
  • Due to the lack of private fields, you can’t have a method called name() that returns a field called name - one will overwrite the other in this style

The limits of inheritance

Even ignoring these issues (and there are other ways to create objects that work around this limitation), object oriented inheritance is not the most flexible way to design software. For many domains of problems, composition or augmentation trumps inheritance. Let’s examine a motivating example. In the following class diagram we have some domain objects for a game. Players and Mobs are Person(s), Person(s) and Rucksacks are Objects. All is well with this view of the world, no code is duplicated between objects and it all works as intended.

Here are some unit tests of these objects to illustrate their functionality:

var JS = require("jstest"),
    components = require("../lib/components");

JS.Test.describe("components", function() {
    this.before(function() {
        this.rucksack = components.rucksack({name: "Bag of Holding"});
        this.another_rucksack = components.rucksack({name: "Bigger bag"});
        this.spell = components.spell({name: "Potion of empathy"});
    });

    this.describe("container", function() {
        this.it("can contain other containers", function() {
            this.rucksack.add(this.spell);
            this.another_rucksack.add(this.rucksack);
            this.assertEqual(1, this.another_rucksack.quantity());
            this.assertEqual("Bag of Holding",
                             this.another_rucksack.items()[0].name());
        });
        this.it("can contain other objects", function() {
            this.rucksack.add(this.spell);
            this.assertEqual(1, this.rucksack.quantity());
            this.assertEqual("Potion of empathy",
                             this.rucksack.items()[0].name());
        });
    });
});

Now say we want to extend our player objects to have an inventory (or container). We could approach this in a number of ways but inheritance is not one of them. We already have rucksack and person inheriting from object so we can’t just create container as a superclass of those. We need a way to extend both those objects and extract the container-related behaviour and state so we don’t have code duplication. In Ruby, we might create container as a Module and include it in our classes.

The method pattern in JS allows us to do something quite similar, but first, let’s write a failing test:

this.describe("player", function() {
    this.it("has an inventory", function() {
        this.assertEqual(0, this.player.quantity());
        this.player.add(this.spell);
        this.assertEqual(1, this.player.quantity());
        this.assertEqual("Potion of empathy", this.player.items()[0].name());
    });
});

The Module Pattern

Now we can go ahead and examine how to construct the relationship between objects that we need. This diagram approximates the idea (I’m not exactly clear what options to pass GraphViz to make the container obviously indicate it is included as a module):

The basic ‘shape’ of an object using ‘functional composition’ looks like the following:

var object = function(spec) {
    var that = {};

    that.name = function() {
        return spec.name;
    };

    return that;
};

The spec argument is the object to be augmented (sometimes that’s as simple as being the parent object). The that object which gets returned constitutes the public interface that this object returns. Because spec is passed in and that is returned, this object constructor is a base class.

The container constructor function looks like this:

var container = function(that) {
    var inventory = [];

    that.add = function(item) {
        inventory.push(item);
    };

    that.quantity = function() {
        return inventory.length;
    };

    that.items = function() {
        return inventory;
    };

    return that;
};

Unlike the previous example, container exists to decorate the provided object with more functionality. If you needed to provide defaults for this object specifically, you can pass a spec argument in also but it wasn’t necessary here. Crucially, the inventory variable is lexically scoped to the container constructor, meaning no code gets access to it except that inside the container constructor. This is in contrast to Modules in Ruby where everything, fields and all, gets included into a Class and becomes part of that class.

The person object is a little different:

var person = function(spec) {
    var that = container(object(spec)),
        empathy = spec.empathy || 0;

    that.change_empathy = function(amount) {
        empathy = empathy + amount;
    };
    that.empathy = function() {
        return empathy;
    };

    return that;
};

We’re back to using a spec argument here so that we can instantiate a person with some default amount of empathy. The that = container(object(spec)) stands in for traditional object inheritance.

After we’ve created the container object and wired it up to person and rucksack, the code for concrete objects in our toy example looks like this:

module.exports.rucksack = function(spec) {
    var that = container(object(spec));
    return that;
};

module.exports.mob = function(spec) {
    var that = person(spec);
    return that;
};

module.exports.player = function(spec) {
    var that = person(spec);
    return that;
};

module.exports.spell = function(spec) {
    var that = object(spec);

    that.affect = function(obj) {
        if (that.inRange(obj)) {
            obj.change_empathy(1);
        }
    };

    return that;
};

Although the diagram above implies a strict inheritance hierarchy, that is not necessarily the case. The difference between object and container is subtle, with object hiding the spec object and returning a new object (just as person and rucksack do), whereas container just adds methods to an object.

One last thing! It might be apparent, but it’s worth calling out the other advantage this gives us: dynamic runtime extensions. If you want the spell object to be able to contain items, you could do so at any stage, running:

var my_spell = spell({name: "Confusing potion"});
// my_spell doesn't have an inventory
container(my_spell);
console.log(my_spell.quantity()); // '0'

This code modifies the my_spell variable, but no other variables that use the spell constructor function. This could be useful in games where items can interact, giving each other different functionality (e.g. imagine a potion that allows bookcases to float or drinking a flying potion adds the flying component to a player). In those cases, there’s no need for complex if/else/switch-style programming, just augment the object with the desired module and that’s it!