Last week we saw how the module pattern could help us to structure our JavaScript code. Let’s recapitulate what this pattern has to offer:

  • Avoids polluting  the global scope
  • Simulates the concept of public/private methods

The above goals, desirable as they may be, come, however, at a price. As is usually the case with software development, there is no magic silver bullet that can cut through all the problems: many times we have to settle for a compromise; we win something, but at a cost.

I am not saying that we should not use the module pattern, I am just saying that it has  disadvantages that we must understand, to be able to apply this pattern correctly.

Difficulties re-using a public function from other public functions

This problem is one of the reasons that led to the adoption of the revealing module pattern:

The Revealing Module pattern came about as Heilmann was frustrated with the fact that he had to repeat the name of the main object when we wanted to call one public method from another or access public variables.

What is he talking about when he says he has “to repeat the name of the main object when we wanted to call one public method from another”?

Take the following example, try to run it, and observe what happens:

var ShoppingCart = (function () {

  var  items = [];

  return {
    printShoppingCart: function(isVerbose)
    {
      console.log("Number of items in cart: "+items.length);
      if (isVerbose)
        console.log("Items: "+items);
    },
    addItem: function (a) {
      items.push(a);
      printShoppingCart(false);
    },
    removeItem: function (a)
    {
      items.splice(items.indexOf(a),1);
      printShoppingCart(false);
    }
  };
  })();

ShoppingCart.addItem("book");

If you execute the code above you will obtain this outcome:

 ReferenceError: printShoppingCart is not defined

When using the module pattern, if you try to call a public function from a public function you will run into the function is not defined error.

You can solve this error by prefixing the call to the public method with the name of the main object like this:

var ShoppingCart = (function () {

  var  items = [];

  return {
    printShoppingCart: function(isVerbose)
    {
      console.log("Number of items in cart: "+items.length);
      if (isVerbose)
        console.log("Items: "+items);
    },
    addItem: function (a) {
      items.push(a);
      ShoppingCart.printShoppingCart(false);
    },
    removeItem: function (a)
    {
      items.splice(items.indexOf(a),1);
      ShoppingCart.printShoppingCart(false);
    }
  };
  })();

ShoppingCart.addItem("book");

This way you will obtain the expected result:

Number of items in cart: 0
Number of items in cart: 1

If you had a module with a large number of public functions that are reused in other functions prefixing the call with the object name can become quickly tiresome (and error inducing).

It´s difficult to change the visibility of the methods

In the example above, if at some point in time we decide we want to change the visibility of printShoppingCart from public to private, we must also modify the functions that call it.

To make this crystal clear let’s just move the printShoppingCart method out of the return block into the function private scope:

var ShoppingCart = (function () {

  var  items = [];

  function printShoppingCart(isVerbose)
    {
      console.log("Number of items in cart: "+items.length);
      if (isVerbose)
        console.log("Items: "+items);
    }

  return {
    addItem: function (a) {
      items.push(a);
      ShoppingCart.printShoppingCart(false);
    },
    removeItem: function (a)
    {
      items.splice(items.indexOf(a),1);
      ShoppingCart.printShoppingCart(false);
    }
  };
  })();
ShoppingCart.addItem("Book");

As you may have guessed the snippet above results in an error again:

ReferenceError: items is not defined

To make the code work we have to update the public methods addItem, and removeItem like this:

var ShoppingCart = (function () {

  var  items = [];

  function printShoppingCart(isVerbose)
    {
      console.log("Number of items in cart: "+items.length);
      if (isVerbose)
        console.log("Items: "+items);
    }

  return {
    addItem: function (a) {
      items.push(a);
      printShoppingCart(false);
    },
    removeItem: function (a)
    {
      items.splice(items.indexOf(a),1);
      printShoppingCart(false);
    }
  };
  })();
ShoppingCart.addItem("Book");

Methods added later cannot access private functions

This is going to severely limit the possibility to extend or patch a module.

We cannot add/patch public methods that reference private variables

To illustrate this point let’ s try to add a new public method to our ShoppingCart module to get the number of items in the cart:

ShoppingCart.getNumberOfItemsInCart = function()
{
 return items.length;
}
ShoppingCart.getNumberOfItemsInCart();

As expected we run into the usual error:

ReferenceError: items is not defined

We can minify the impact of this issue by adding public get methods for our private variables whenever we use the module pattern to implement our modules:

var ShoppingCart = (function () {
  var items = [];
  function printShoppingCart(isVerbose) {
    console.log("Number of items in cart: "+items.length);
    if (isVerbose)
      console.log("Items: "+items);
  } return {
    getItems: function(){
      return items;
    },
    addItem: function (a) {
      items.push(a);
      printShoppingCart(false);
    },
    removeItem: function (a) {
      items.splice(items.indexOf(a),1);
      printShoppingCart(false);
    }
  };
})(); 

ShoppingCart.getNumberOfItemsInCart = function()
{
 return ShoppingCart.getItems().length;
}

ShoppingCart.addItem("Book");
console.log(ShoppingCart.getNumberOfItemsInCart());

We can´t patch private methods

In the example above if there is a bug in the private method printShoppingCart, we are pretty much up the creek. If the module we are using is a third party library, we don´t want to go ahead and modify the original code of the library. Modifying the original code would make it difficult to update the library later when a new version was released, because we would have to keep track of the changes we made, and merge them again.

Usually if we find ourselves in that situation we would try to patch the function:

ShoppingCart.printShoppingCart = function(isVerbose)
{
  console.log("New version 1.1");
}
//So far so good
ShoppingCart.printShoppingCart(true);
// But look at this:
ShoppingCart.addItem("Book");

We patched the printShoppingCart, except that we really did not: we have merely added a new public function called also printShoppingCart  but our addItem method is still pointing to the original private printShoppingCart.

So we now have two options: modify the original code, or patch not the private function, which was our original intention, but patch all the public functions that interact with the offending private function:

ShoppingCart.printShoppingCart = function()
{
  console.log("New version 1.1");
  console.log("Number of items in cart: "+ShoppingCart.getItems().length);
  console.log("Items: "+ShoppingCart.getItems());
}

ShoppingCart.addItem = function(a)
{
  ShoppingCart.getItems().push(a);
  ShoppingCart.printShoppingCart();
}

ShoppingCart.removeItem = function(a)
{
  ShoppingCart.getItems().splice(ShoppingCart.getItems().indexOf(a),1);
  ShoppingCart.printShoppingCart();
}

As we have seen, while the module pattern is very useful it can also lead to complicated situations if applied in a thoughtless manner.

In order to address some of the issues inherent to using the module pattern some other implementation variants were proposed. The revealing module pattern proposes an improvement over the two first points we have seen in this article, but it brings its own set of problems.

We´ll see that soon.

Until then, thanks for reading :-), and remember: if you want to receive a notification when new posts are written, you can click at the “follow” button at the bottom.