JavaScript, Assumption Inversion Principle and modularity

On past posts (1, 2) I tried to described what I call Assumption Inversion Principle. Well, talk is cheap, show me the code!

I’m trying to apply AIP on a personal project without infecting (much) the global namespace and going only with raw JavaScript to avoid unnecessary dependencies on things like RequireJS.

That’s the template I have come up with so far (gist):

/*
File...: foo.js
Exports: bar, baz
Depends: spam, eggs
*/
;(function (namespace) {
 'use strict'

 namespace.foo = function (namespace) {
   /* Dependencies */
   var spam = namespace.spam
   var eggs = namespace.eggs

   function bar () {
   }

   function baz () {
   }

   /* Exports */
   namespace.bar = bar
   namespace.baz = baz
 }
}((function () {
  if (typeof window === 'undefined') {
    return module.exports
  } else {
    return window
  }
}())))

The file can be used by the browser or as a NodeJS module. At the browser it will register an initialization function on “window” and as a NodeJS module it exports the initialization function.

How to use it? Just include every module and use each initialization function to populate your namespace:

/*
 * Namespace of our app.
 */
var myApp = {}

/* 
 * Initialize 'fooDeps' module
 * e.g. registers 'spam' and 'eggs' on namespace
 */
fooDeps(myApp)

/* 
 * Initialize 'foo' module
 * Uses 'spam' and 'eggs' and registers 'foo' and 'bar'
 */
foo(myApp)

And what do I gain with it? Well, I can treat modules as first-class citizens. Instead of bloating the code with objects and pretty patterns as singletons and factories, each module code knows how to initialize itself, so they are pretty straight forward. As they only use assumptions about their dependencies, the module code easy adapts to other environments. Classic example on single project: production code uses the real dependencies and test code uses mocks or simples objects. On multiple projects, the implementation of each dependency can be selected on whatever reason.

The particular project I’m working on is a Word Finder style game. We use a word dictionary and a random letter generator to populate the board. The random letter generator uses the ratio of frequency of each letter at the dictionary to select the random letters with the same ratio (gist):

;(function (namespace) {
  'use strict'

  namespace.randomlettergenerator = function (namespace) {
    var total = 0
    var lettersArray = []

    /*
     * Populates "lettersArray" and computes "total".
     */
    ;(function () {
      var letters = {}
      namespace.dictionaries.forEach(function (word) {
        var i, len, letter
        for (i = 0, len = word.length; i < len; i++) {
          letter = word[i]
          if (typeof letters[letter] === 'undefined') {
            letters[letter] = { letter: letter, count: 1 }
            lettersArray.push(letters[letter])
          } else {
            letters[letter].count += 1
          }
          total += 1
        }
      })
    }())

    /*
     * Sorts by descending letter frequency
     */
    lettersArray.sort(function (a, b) {
      if (a.count > b.count) {
        return -1
      }
      if (a.count < b.count) {
        return 1
      }
      return 0
    })

    /**
     * Random letter generator.
     */
    function RandomLetterGenerator () {
      this.lastIx = -1
    }

    /**
     * Returns a random letter.
     *
     * @return Random letter.
     */
    RandomLetterGenerator.prototype.next = function () {
      var i = this.lastIx
      var len = lettersArray.length
      var count = Math.random() * total

      while (count > 0) {
        i++
        if (i >= len) {
          i = 0
        }
        count -= lettersArray[i].count
      }

      this.lastIx = i
      return lettersArray[i].letter
    }

    namespace.RandomLetterGenerator = RandomLetterGenerator
  }
}((function () {
  if (typeof window === 'undefined') {
    return module.exports
  } else {
    return window
  }
}())))

Notice the module initialization code, it is possible to mock out or use multiple dictionaries to initialize different namespaces with different versions of RandomLetterGenerator class. The same effect is possible using pure OO factory pattern.

Other modules are responsible for finding words and checking board properties. The dictionaries are always an assumption, so the code is easily testable, even without objectifying everything.

I’m currently feeling good with the code. Not sure how it will scale.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s