Metaduck

The Module Pattern And Inheritance: A Better Way

Recently I blogged about how I like to build my JavaScript “classes”. I personally prefer the module pattern: it provides true encapsulation and doesn’t expose unnecessary methods and properties, which you get when you use the typical prototype pattern.

One of the downsides of it, I’ve been told, is that it doesn’t easily allow to build classes that you can inherit from. That is true if you mix the styles, specially if you want to inherit from a class that is built this way. But if you are willing to use this technique throughout all the inheritance chain, I will demonstrate that it provides a much more powerful and sane way of doing inheritance.

One of the fallacies of using inheritance in JavaScript in the typical prototypical way - and in the model of most OO programming languages I know - is that inheritance, as it is, is a leaky abstraction. Most of the time you have to be aware of some of the implementation details of the super class to be able to inherit from it properly. For instance, since the entire prototypical chain uses the object that is bound to this, you have to be careful that you don’t override any of the properties that are being used down the chain.

For instance, if you are inheriting from Node.js EventEmitter pseudo-class, you have to be careful to never override any of the properties it initializes and changes.

Let me give you an example of where this fails:

Let’s say you have an Animal class:

function Animal() {
  this.walked = false;
}

Animal.prototype.walk = function walk() {
  console.log('walking...')
  this.walked = true
}

Animal.prototype.hasBeenWalked = function hasBeenWalked() {
  return this.walked
}

module.exports = Animal

And let’s say you want to inherit from it. If you save the previous module into a local file named “animal.js”, you can then inherit from it and build a Cat specialization like this:

var Animal = require('./animal')
  , inherits     = require('util').inherits
  ;

function Cat() {
  Animal.call(this);
}

inherits(Cat, Animal);

Cat.prototype.pat = function pat() {
  console.log('purr');
};

Cat.prototype.lasagna = function() {
  console.log('lasagna!');
  this.walked = true;
};

module.exports = Cat;

I introduced an error here: the Cat.prototype.lasagna method sets a the this.walked property to true. Now if you use this cat class and don’t call the lasagna method, it all works fine:

> var Cat = require('./cat')
undefined
> var garfield = new Cat()
undefined
> garfield.hasBeenWalked()
false
> garfield.walk()
walking...
undefined
> garfield.hasBeenWalked()
true

But if you call the lasagna method, it all goes wrong:

> var Cat = require('./cat')
undefined
> var garfield =new Cat()
undefined
> garfield.hasBeenWalked()
false
> garfield.lasagna()
lasagna!
undefined
> garfield.hasBeenWalked()
true

This happens because the cat object is being used like a property global namespace for storing all and every property of this object. Global namespaces tend to become overcrowded and clashes happen if there is no alternative.

Fortunately, there is an alternative.

Using the Module Pattern

Let’s do the previous example using the module pattern:

function Animal() {

  var walked = false

  function walk() {
    console.log('walking...')
    walked = true
  }

  function hasBeenWalked() {
    return walked
  }

  return {
    walk: walk
  , hasBeenWalked: hasBeenWalked
  }
}

module.exports = Animal

Now that we have an Animal class, let’s build the Cat one:

var Animal = require('./animall')

function Cat() {
  var cat = {}
  var walked = false

  cat.__proto__ = Animal()

  cat.pat = function pat() {
    console.log('being patted')
  }

  cat.lasagna = function lasagna() {
    console.log('Lasagna!')
    walked = true
  }

  return cat
}

module.exports = Cat

Now, if you instantiate a Garfield and then invoke the mischieving lasagna method, it all still works:

> var Cat = require('./cat')
undefined
> var garfield = Cat()
undefined
> garfield.hasBeenWalked()
false
> garfield.lasagna()
Lasagna!
undefined
> garfield.hasBeenWalked()
false
> garfield.walk()
walking...
undefined
> garfield.hasBeenWalked()
true

Now, in this case, for every object instance you actually have two separate objects: the animal object and the cat object.

It’s like our brains: the lizzard brain, the mammal brain and the human brain. They’re all layered on top of each other, overriding the lower parts, but they do not use the same space.