Design Patterns

In this article by Ovais Mehboob Ahmed Khan, author of the book JavaScript for .NET Developers, we will see that in every mid to large sized projects, good architecture and design always plays an important role in handling complex scenarios and increasing maintainability of the product. Design pattern are best practices developed and used by professional developers to solve a particular problem. If it has been used in the application for specific scenarios, it evades many of the issues that one could face during development or when running the application in production. Design patterns solve the problems by providing guidelines that are industry best practices to handle problems or achieve or implement any requirement. Singleton pattern, for example, is used to create only one instance that is shared among all; whereas, prototype is used to extend the existing functionality of an object by adding more properties, methods, and so on. Design patterns are classified into three categories creational, structural, and behavioral patterns. The topics that we will cover in this article are as follows:

  • Observer pattern
  • Pub/sub pattern
  • Promises

(For more resources related to this topic, see here.)

Observer pattern

Observer pattern is widely used to implement publisher/subscriber model, in which if a state of any object changes, it notifies all the subscribed observers. The observer pattern has three methods to add, remove, and notify observers. The observer pattern is shown in the following diagram:

The following is the code snippet that implements the observer pattern:

<script>

  //Helper function used by the Observer implementors
  var HelperFunction = function (type) {

    var txtEntered = document.getElementById("txtEntered");

    var englishText = document.getElementById("englishText");
    var frenchText = document.getElementById("frenchText");
    var chineseText = document.getElementById("arabicText");

    if (type == "english") {
      englishText.innerText = txtEntered.value;
    } else if (type == "french") {
      frenchText.innerText =
        translateTextToFrench(txtEntered.value);
    } else if (type == "arabic") {
      arabicText.innerText =
        translateTextToArabic(txtEntered.value);
    }
  }

  var EnglishTranslator = {
    update: function () {
      //Call helper function to change text to English
      HelperFunction("english");
    }
  }

  var FrenchTranslator = {
    update: function () {
      //Call helper function to change text to French
      HelperFunction("french");
    }
  }

  var ArabicTranslator = {
    update: function () {
      //Call helper function to change text to Arabic
      HelperFunction("arabic");
    }
  }

  //Observer function that contains the list of observer handlers
  function Observer() {
    this.observers = [];
  }

  //to add observer
  Observer.prototype.addObserver = function (object) {
    console.log('added observer: ' + object);
    this.observers.push(object);
  };

  //to remove observer
  Observer.prototype.removeObserver = function (object) {
    console.log("removing observer");
    for (i = 0; i < this.observers.length; i++) {
      if (this.observers[i] == object) {
        this.observers.splice(object);
        return true;
      }
    }
    return false;
  };

  //To notify all observers and call their update method
  Observer.prototype.notify = function () {
    for (i = 0; i < this.observers.length; i++) {
      this.observers[i].update();
    }
  }

  //Adding objects as observers that implements the update method
  var observer = new Observer();
  observer.addObserver(EnglishTranslator);
  observer.addObserver(FrenchTranslator);
  observer.addObserver(ChineseTranslator);

  //Execute will be called on button click to notify observers
  var execute = function () {
    observer.notify();
  };

</script>
<body>
  <div>
    Specify some text: <input type="text" id="txtEntered" />
    <input type="button" onclick="execute()" value="Notify" />
  </div>
  <div>
    <span id="englishText"></span>
    <span id="frenchText"></span>
    <span id="arabicText"></span>
  </div>
</body>

In this example, we have taken a scenario that translates the text for all the languages added as an observer objects. The Observer() function contains three functions, namely addObserver(), removeObserver(), and notify(). Here, addObserver() is used to add any object in an observer's list, removeObserver() is used to remove a specific object from the observer list, and notify() executes the observer's update() method. The EnglishTranslator, FrenchTranslator, and ArabicTranslator are objects that have implemented the update() method, which is called when notify() is executed. On the page load, we have registered all the translator objects as observers and provided a textbox with a button through which the user can type any text on the textbox, and on a button click's event, it will call the observer notify() method that eventually calls the registered observer's update() method.

Pub/sub pattern

The pub/sub pattern is an alternative pattern like an observer pattern with a slight difference in its implementation. In the observer pattern, an observer object can invoke the notify() method of all the observers, whereas in publisher/subscriber pattern, there is a centralized event system that is used to publish events to the subscribers. In this pattern, publisher and subscribers are loosely tied and both publisher and subscriber don't know to whom the message was sent or who received it.

The following example implements a pub/sub pattern that holds a two-dimensional array. The user can add events based on the event name and callback function. It has three methods: subscribe() to subscribe events in an events array, unsubscribe() to remove events from an array, and publish() to call the callback functions for a specific event name. The example can be depicted as follows:

The following is the code snippet to implement the pub/sub pattern in JavaScript:

var PubSub = function () {
  this.events = [];
  this.subscribe = function (eventName, func) {
    this.events[eventName] = this.events[eventName] || [];
    this.events[eventName].push(func);
  };

  this.unsubscribe = function (eventName, func) {
    if (this.events[eventName]) {
      for (i = 0; i < this.events[eventName].length; i++) {
        if (this.events[eventName][i] === func) {
          this.events[eventName].splice(i, 1);
          break;
        }
      }
    }
  };

  this.publish = function (eventName, data) {
    console.log(data);
    if (this.events[eventName]) {
      this.events[eventName].forEach(function (event) {
        event(data);
      })
    }
  };
};

var execute = (function () {
  var pubSub = new PubSub();
  pubSub.subscribe("myevent1", function () {
    console.log("event1 is occurred");
  });

  pubSub.subscribe("myevent1", function () {
    console.log("event1 is occurred");
  });

  pubSub.subscribe("myevent2", function (value) {
    console.log("event2 is occurred, value is "+ value);
  });

  pubSub.publish("myevent1", null);

  pubSub.publish("myevent2", "my event two");
})();

In this example, we have one PubSub object that provides three methods to subscribe, unsubscribe, and publish events. The subscribe() method is used to subscribe to any event and takes two parameters such as event name and function and add them in an array for specific event name. If the event name does not exist, a new array will be initialized for this event name, otherwise the existing instance will be retrieved to add the item. The user can register as many events by passing the event name and anonymous function body that will be executed when the event is published. To publish events, the publish() method can be called, which takes the event name and the data you want to pass to the corresponding function executed.

Promises

Promises are one of the most popular pattern extensively used in JavaScript APIs and frameworks to make asynchronous calls simpler. Asynchronous operation in JavaScript needs to have a callback function register that invokes when the value is returned. With promises, when you make any asynchronous call, it immediately returns a promise and provide objects such as then and done to define a function when the resultant value is resolved. In the real world, promises are just like the token or receipt of the food you order in a fast food restaurant and that receipt guarantees to have the food delivered when it is ready. Promises are tokens that confirm to get the response on a specific request. An example of promise pattern is shown in the following image:

In JavaScript, promises are widely used by APIs and frameworks such as AngularJS, Angular 2, and more. Let's take a look at the following example that implements the promise pattern:

//Defining Promise that takes a function as a parameter.
var Promise = function (func) {
  //Declared member variable
  var callbackFn = null;

  //Expose done function that can be invoked by the object
    returning promise
  //done() function takes a callback function which can be defined
    when using done method.
  this.done = function (callback) {
    callbackFn = callback;
  };

  function resolve(value) {
    setTimeout(function () {
      callbackFn(value)
    },3000)
  }

  //Here we are actually executing the function defined when
    initializing the promise below.
  func(resolve);
}

//Object that is used to order food and returns a promise
var orderFood = function (food) {
  //returns the Promise instance and pass anonymous function that
    call resolve method which actually serve the request after
    delaying 3 seconds.
  return new Promise(function (resolve) {
    resolve(food);
  });
}

//Initialized orderFood that returns promise
var order = new orderFood("Grilled Burger");
//Calling done method which will be invoked once the order is
  ready
order.done(function (value) {
  console.log(value);
});

In this example, we developed a Promise() function that contains done() and resolve() methods, where done() is used to invoke the callback function implemented by the consumer object and resolve() called internally to execute the actual task. The second function is orderFood() that returns a promise object and calls the resolve() method to actually run the task when orderFood() is initialized by the consumer. The following screenshot shows how this code is executed:

If you have noticed, we used the setTimeout() function to delay the response by three seconds in the previous example. We will use setTimeout() to represent an asynchronous scenario where setTimout() and the registering of the done() callback handler will be executed in parallel.

The preceding code snippet is a very basic implementation of promise pattern. However, there are other parts of promises that we can implement to make it robust. Promises have states and the following is the modified version that not only maintains the states for inprogress, done, and failed but also provide the failed handler to catch exceptions. The following is the description of the states:

  • inprogress: When resolve() is called, the state will be set to inprogress. This state will persist until we register the handlers for done and failed scenarios.
  • done: When done() is invoked, the status will be set to done.
  • failed: When any exception occurs, the status will be set to failed.

The following is the modified version of the order food example:

//Defining Promise that takes a function as a parameter.
var Promise = function (func) {

  //Default status when the promise is created
  var status = 'inprogress';
  var error = null;

  //Declared member variable
  var doneCallbackFn = null;
  var failedCallbackFn = null;

  //Expose done function that can be invoked by the object
    returning promise
  this.done = function (callback) {
    //Assign the argument value to local variable
    doneCallbackFn = callback;
    if (status === "done") {
      doneCallbackFn(data);
    } else {
      doneCallbackFn(status);
    }
    //return promise to register done or failed methods in chain
    return this;
  };

  //Expose failed function to catch errors
  this.failed = function (callback) {
    if (status === "failed") {
      failedCallbackFn(error);
    }
    //return promise instance to register done or failed methods
      in chain
    return this;
  };

  function prepareFood() {
    setTimeout(function () {
      status = "done";
      console.log("food is prepared");
      if (doneCallbackFn) {
        doneCallbackFn(data);
      }
    }, 3000);

  }

  function resolve(value) {
    try {
      //set the value
      data = value;

      //check if doneCallbackFn is defined
      if (doneCallbackFn) {
        doneCallbackFn(value);
      }
      prepareFood();

    } catch (error) {
      //set the status to failed
      status = "failed";
      //set the exception in error
      error = error;
      //check if failedCallbackFn is defined
      if (failedCallbackFn) {
        failedCallbackFn(value);
      }
    }
  }
  //Here we are actually executing the function defined when
    initializing the promise below.
  func(resolve);
}

//Object that is used to order food and returns a promise
var orderFood = function (food) {
  //returns the Promise instance and pass anonymous function that
    call resolve method which
  //actually serve the request after delaying 3 seconds.
  return new Promise(function (resolve) {
    resolve(food);
  });
}

//Initialized orderFood that returns promise
var order = new orderFood("Grilled Burger").done(
  function (value) { console.log(value); }).failed(
  function (error) { console.log(error);})

Summary

In this article, we discussed some of the design patterns used in JavaScript There are various more design patterns available and documented, which can be referred at http://www.dofactory.com/javascript/design-patterns.

For more information on design patterns, refer the following book by Packt Publishing:

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

JavaScript for .NET Developers

Explore Title