A useful JavaScript pattern - The Module Pattern

Introduction

A good API design forms an integral part of coding best practices. And what is a good API? A good API would only expose methods that would be needed by the client using the API. The public space would not be cluttered by unnecessary methods. And there are not many times where I have seen JavaScript developers really caring about a good API design. The reason probably is that people don’t take JavaScript seriously. Anyway, that is another topic, which might require a separate blog post of its own.

Module pattern

So coming back to the topic, I would be discussing a useful pattern that will help in achieving a good API design. This pattern is called module pattern.
This pattern allows you to hide helper methods and properties by declaring them as private, while public methods and properties are returned as the return value of an anonymous function that is executed immediately after its declaration.

Implementation

Consider you are creating an API for a gallery plugin, then following is how you would go about doing it using the module pattern, take special note of how we have not polluted the public space.

var My_gallery = function()
{
  //private properties
  var gallery;
  var item_container;
  var preview_img;
  var preview_caption;
  var num_items;
  
  // private methods
  var init_click_handlers = function()
  {
    item_container.find( 'li' ).click(function()
    {
      show_item( this );
    });
  }
  
  var preload_images = function()
  {
    item_container.find( 'img' ).each(function(index, element)
    {
      var img = new Image();
      img.src = $( element ).attr( 'large_src' );
    });
  }
  
  // return the public properties and methods
  return {
    init : function()
    {
      // public methods can call private properties directly
      gallery         = $( '#gallery_container' );
      item_container  = gallery.find( '#items' );
      preview_img     = gallery.find( '#preview_img' );
      preview_caption = gallery.find( '#preview_caption' );
      num_items       = item_container.find( 'li' ).length;
      
      // public methods can call private methods directly
      init_click_handlers();
      preload_images();
    },
    
    add_item : function(thumb_src, caption, large_src)
    {
      var new_item = gallery.find( 'li' ).first().clone();
      new_item.find( 'img' )
        .attr({
          src       : thumb_src,
          large_src : large_src,
          alt       : caption
        })
        .click(function()
        {
          // public method has to be called by its fully qualified name
          My_gallery.show_item( this );
        });
      
      gallery.find( 'li' ).append( new_item );     
    }
    
    show_item : function( item )
    {
      preview_img.src( item.attr( 'large_src' );
      preview_caption.text( item.attr( 'alt' );
    }
    
    remove_item : function( item )
    {
      gallery.find( item ).empty();
    }
    
  }
  
}(); // this function is executed as soon as it is declared

The API of this plugin contains the following methods:

init();
add_item();
show_item();
remove_item();

See how we have called the function immediately after its declaration, this ensures that the methods returned are immediately added to the My_gallery object. An added advantage is that there are no global functions or properties, which means there would be no naming conflicts.

Typically these public methods would be called as follows:

$( window ).load(function()
{
  My_gallery.init();
  My_gallery.add_item( 'thumb.png', 'This is a really good image', 'large.png');
});

Conclusion

When using the module pattern, don’t return those too many methods, as that will pollute the public space, only return those that will be used by the client when calling the API. The public methods and properties have to be called by their fully qualified name, while the private methods and properties don’t require a fully qualified name, they have to be called by their names, since they are only available within the body of the anonymous function and are not visible outside.

You might want to take a look at one of my previous posts to learn more about the anonymous function trick.