JavaScript Design Patterns – How to Make Sense of Chaos Pt2

JavaScript Design Patterns – How to Make Sense of Chaos Pt2-small

Table of Contents

How about to master some JavaScript design patterns? It’s been couple days since I wrote the first part of this mini-series. Thank you for your patience. Today, you are going to explore and learn about the rest of commonly used design patterns by JavaScript developers. In end of this post, you will also find a brief summary of the nine patterns we’ve discussed in this mini-series. Without further ado, let’s finish this series and turn yourself into true JavaScript master!

Introduction and first 4 patterns are in part 1.

Journey to JavaScript Design Patterns Continues

Strategy

The first of JavaScript design patterns we will follow the first part is called Strategy. What is the best usage of strategy design pattern? It is any situation when you need or want to keep the same interface and also allow to select the best algorithm (strategy) to handle the specific task the user wants to get done at the same time. With this approach, client can then handle tasks depending on context or their intentions. I guess that illustrating this theory on some easy example would be the best way to help you understand it.

// Creating context for using strategies
var Delivery = function() {
 this.service = '';
};

Delivery.prototype = {
 useService: function(service) {
  this.service = service;
 },
 calculate: function(package) {
  return this.service.calculate(package);
 }
};

// Creating the first strategy called 'Post'
var Post = function() {
 // Function for calculating the price
 this.calculate = function(package) {
  return '$13.00';
 };
};

// Creating the second strategy called 'Messenger'
var Messenger = function() {
 // Function for calculating the price
 this.calculate = function(package) {
  return '$21.35';
 };
};

// Creating the third strategy called 'Uber'
var Uber = function() {
 // Function for calculating the price
 this.calculate = function(package) {
  return '$36.75';
 };
};

// Creating the fourth strategy called 'Airline'
var Airline = function() {
 // Function for calculating the price
 this.calculate = function(package) {
  return '$44.50';
 };
};

// Creating the fifth strategy called 'Teleportation'
var Teleportation = function() {
 // Function for calculating the price
 this.calculate = function(package) {
  return '$64.50';
 };
};

function test() {
 // Creating new package to be delivered using our four strategies
 var order = {
  from: '14700',
  to: '34200',
  weigth: '1kg'
 };

 // Creating new instances of our three strategies
 var post = new Post();
 var messenger = new Messenger();
 var uber = new Uber();
 var airline = new Airline();
 var teleportation = new Teleportation();

 // Creating new instance of a delivery object
 var delivery = new Delivery();

 // Testing 'Post' strategy
 delivery.useService(post);
 console.log('Post option (strategy) will cost: ' + delivery.calculate(order));

 // Testing 'Messenger' strategy
 delivery.useService(messenger);
 console.log('Messenger option (strategy) will cost: ' + delivery.calculate(order));

 // Testing 'Uber' strategy
 delivery.useService(uber);
 console.log(“Uber option (strategy) will cost: ' + delivery.calculate(order));

 // Testing 'Airline' strategy
 delivery.useService(airline);
 console.log('Airline option (strategy) will cost: ' + delivery.calculate(order));

 // Testing 'Teleportation' strategy
 delivery.useService(teleportation);
 console.log('Teleportation option (strategy) will cost: ' + delivery.calculate(order));
}

// Initiating test function
test();

To summarize this example … You have an order you want to be delivered from your company’s store to one of your customers. Next, you have five different delivering services to choose from (strategies) with the focus on getting the best price. In a real-life, this can be an example of, I guess, any e-shop that allows its customers to select the delivery service they prefer. Then, they will receive the cost estimated by individual strategies (services).

Facade

Next member of JavaScript design patterns family is called facade. This one is quite simple. Facade’s main job is to provide alternative interface to existing object. This interface is defined on a higher-level and its purpose is to make the systems under it easier to use. I should also mention that Facade pattern is often used is in refactoring. For example, you might be in a situation where you have some code that should not be visible to the client. Facade can help you “hide” this code under behind new interface.

Then, you (through Facade) can expose only parts of code that are necessary and while keeping the interface easy-to-use and user-friendly. Again, let’s demonstrate this pattern on an example.

// Creating new object called ATM as an interface for clients
var Atm = function(pin) {
 this.pin = pin;
};

Atm.prototype = {
 withdraw: function(amount) {
  var result = 'approved.';
  // hidden sub-systems
  if (!new Transaction().verify(this.pin)) {
   result = 'denied.'; 
  } else if (!new Deposit().check(amount)) {
   result = 'denied.'; 
  } else if (!new Process().run(amount)) {
   result = 'denied.'; 
  }
  console.log(amount + ' for withdrawal was ' + result);
 }
};

var Transaction = function() {
 this.verify = function(pin) {
  // Verifying user's pin
  return true;
 };
};

var Deposit = function() {
 this.check = function(amount) {
  // Checking the bank balance
  return true;
 };
};

var Process = function() {
 this.run = function(amount) {
  // Processing the transaction on the background
  return true;
 };
};

// Creating function to test our ATM
function test() {
 var atm = new Atm('1326');
 var result = atm.withdraw('256000');
}

// Initialization of test function
test();

In our example, the ATM object is the Facade. It works as an interface for some random client. It uses only one method called withdraw to cover more complex API created in the background. So first, our client passes his or her pin code to our ATM constructor function. As a next step, the withdraw method is called with the requested amount of money to be withdrawn. Beneath it, there are three separate subsystems that need to be initiated – Transaction, Deposit and Process.

Depending on few conditions such as pin and bank balance the withdraw request is either accepted or denied. As the last step, there is a console.log to send a message to the user.

Proxy

The description of the proxy design pattern can be as simple as “one object acting as an interface to another object.” Now, you may feel that it sounds familiar with previous design pattern facade. The difference between those two is that when you use proxy you are creating a “middleman” standing between the user of an object and the object itself. This structure then allows you to restrict and protect the access to that object. You can also imagine the proxy as some kind of a patron trying to make the object do as little work as possible.

One example from web development can be limiting the number of HTTP requests and loading assets that might not be necessary. In this case, one of the solutions I like to implement as well is, for example, using lazy loading technique for images. By using this technique (via plug-ins), you can lower the number of requests as well as save some bandwidth. Images are loaded only when user scrolls to specific threshold. By the way, great plugin I use in my projects is made by appelsiini.

However, writing the whole code for lazy load plugin would take too much time and also space. Instead, let’s illustrate the proxy pattern on something simpler, like a to-do list.

// Creating our Proxy
var ToDoList = function() {
 // Don't iniInitialize the todoList at this moment
 this.todoList = null;
};

MigrationList.prototype = {
 // init method is called every time time any other function is called and ToDoList is initialized only when needed.
 init: function() {
  // Check if todoList exists
  if (!this.todoList) {
   // If it todoList doesn't exist, create it
   this.todoList = new ToDoList();
  }
 },

 // Method for adding new item to our todo list
 addItem: function(item) {
  // The first step is to always make call to init() method
  this.init();
  return this.todoList.addItem(item);
 },

 // Method for getting existing item from our todo list
 getItem: function(item) {
  // The first step is to always make call to init() method
  this.init();
  return this.todoList.getItem(item);
 },

 // Method for removing existing item from our todo list
 removeItem: function(item) {
  // The first step is to always make call to init() method
  this.init();
  return this.todoList.removeItem(item);
 },

 // Method for searching for item in our todo list
 search: function(item) {
  // The first step is to always make call to init() method
  this.init();
  return this.todoList.search(item);
 },

 // Method for showing all items in our todo list
 show: function() {
  // The first step is to always make call to init() method
  this.init();
  return this.todoList.show();
 }
};

To summarize the example above, we’ve created a new ToDoList object. Then, we wait with initialization of the todoList until it is really needed. In the next step, we are adding couple new methods to our ToDoList object via its prototype – addItem, getItem, removeItem, search and show. This is the moment when proxy pattern comes into play. Any time one of these methods is called, the first step is to always make a call to inti() method that will create new todoList unless there is already one.

Mediator

Next child from JavaScript design patterns family is mediator. Honestly, this design pattern is not used that often on the JavaScript scene. Anyway, mediator uses loose coupling of objects to help you improve maintainability of your code. It doesn’t allow individual objects to communicate with each other on their own. In other words, when some part of a program need to communicate with another part, like informing this part about some change, it happens through a mediator object. Then, mediator forwards this message to the target object.

Great example to illustrate the mediator pattern is a classroom debate. Let’s create a classroom, register couple students and let them debate on topic of philosophy.

// Create new Student object
var Student = function(name) {
 this.name = name;
 this.classroom = null;
};

// Adding couple methods to Student prototype
Student.prototype = {
 say: function(message, to) {
  this.classroom.say(message, this, to);
 },
 receive: function(message, from) {
  console.log(from.name + ' to ' + this.name + ': ' + message);
 }
};

// Creating new Classrom object
var Classroom = function() {
 // Empty array for students
 var students = {};
 return {
  // Method for registering new students to debate
  register: function(student) {
   student[student.name] = student;
   student.classroom = this;
  },

  // Method to allow communication between students
  say: function(message, from, to) {
   if (to) {
    // If there is only one message
    to.receive(message, from); 
   } else {
    // Broadcast message
    for (var key in students) { 
     if (students[key] !== from) {
      students[key].receive(message, from);
     }
    }
   }
  }
 };
};

function debate() {
 // Creating couple instances of Student objects
 var sonny = new Student('Sonny');
 var johny = new Student('Johny');
 var victoria = new Student('Victoria');
 var andy = new Student('Andy');

 // Creating new instance of Classroom
 var classroom = new Classroom();
 classroom.register(sonny);
 classroom.register(johny);
 classroom.register(victoria);
 classroom.register(andy);
 
 // Starting the debate
 sonny.say('Socrates is the greatest philosopher in the history.', victoria);
 andy.say('What about Aristotle?', sonny);
 sonny.say('Maybe.', andy);
 victoria.say('No, it is Plato.', sonny);
 johny.say('Come on! It is Nietzsche!', victoria);
}

// Initialization of debate function
debate();

To summarize the example, we have four students attending a debate in a classroom. The Classroom is the mediator. Each student is then represented by a Student object and can send message to other student. Classroom will take care about delivering his or her message to specific student.

Observer

Without any delay, let’s talk about the last pattern called observer. Observer creates “observable” objects and promotes loose coupling. These objects then notify all their observers when a specific event occurs. You can imagine it as an object that subscribes (subscriber) to another object’s (publisher) specific activity (feed) and gets notified about every event. It is the publisher who notifies all its subscribers. Publisher can also send some message to subscribers. Sometimes, observer is also called subscriber/publisher pattern.

Let’s illustrate this pattern on simple example.

// Creating new Magazine object
var Magazine = function() {
 // Creating an empty array for subscribers
 this.subscribers = [];
};

// Adding couple methods to Magazine object
Magazine.prototype = {
 // Method for adding new subscriber to subscribers array
 subscribe: function(name) {
  this.subscribers.push(name);
 },
 
 // Method for removing subscribers
 unsubscribe: function(name) {
  // Search the subscriber's name is in subscribers array and remove it.
  for (var i = 0, j = this.subscribers.length; i < j; i++) {
   if (this.subscribers[i] === name) {
    this.subscribers.splice(i, 1);
    // When the name is found, stop the loop
    return;
   }
  }
 },
 
 publish: function(data) {
  // Iterate over the subscribers array and call each of
  // the name functions.
  for (var i = 0, j = this.subscribers.length; i < j; i++) {
   this.subscribers[i](data);
  }
 }
};

// Create new Observer
var Observer = function (article) {
 console.log(article);
};

// Create instance of new magazine
magazine = new Magazine();

// Register Observer
magazine.subscribe(Observer);

// Inform subscribers about new article
magazine.publish('There is a new post: The 80 Wisdom Sayings of the Vikings.');

What we did in our example is simple. We created new online magazine with array for storing new subscribers. Then, we’ve added couple new methods to Magazine object – subscribe, unsubscribe and publish. Next, we’ve also created an Observer object to inform subscribers about new articles. Finally, we’ve created new magazine, registered the observer and announced publishing of a new article. By the way, this article was published on The Art of Manliness.

Quick Summary of JavaScript Design Patterns

Let’s quickly summarize all the JavaScript design patterns you’ve learned and what they do. The first one was singleton. This patterns creates only one object of a hypothetical “class”. Remember, single[one class]-ton. We practiced this pattern on several examples with famous mathematicians and philosophers. Keep in mind that JavaScript doesn’t have classes until ES6, so technically every object in JavaScript is a singleton by default. Also, don’t confuse singleton with module pattern.

The second member of JavaScript design patterns family was a factory. This one is basically a method that creates objects and performs similar operations while doing it, just like the factory. Third pattern was iterator. Iterator allows you to access data stored in some collector object without necessarily exposing these data or their structure. You can think about it as some kind of an API. Fourth pattern was decorator. The purpose of this pattern is to add additional functionality to object’s behavior (decorate it) or tweak current functionalities on the fly. These were the patterns we discussed in the first part.

In the second part, we started with design pattern called strategy. The main idea behind strategy was basically to keep the same interface while allowing to select the best algorithm (strategy) to handle the specific task the user wants to get done. Next was facade. The goal of facade is to provide alternative interface to your object. It follows good rule of thumb that you should always keep your methods short and specialized. Meaning, avoid letting one method handle too much work. Another pattern you learned about was proxy.

In proxy pattern, you have at least two objects and one object functions as an interface to the another one. Unlike facade, proxy is positioned between the client of an object and the object itself. This way, it can limit the access to that object and protect it from accidental changes. Proxy also helps you avoid expensive operations. This is done either by “batching” those operations or by performing only those that are necessary and only when they are necessary.

The last but one of JavaScript design patterns was mediator. Mediator uses loose coupling of objects to help you improve maintainability of your code. Meaning, it doesn’t allow individual objects to communicate with each other directly. All necessary communication happens only through a mediator object. When any change happen, it is mediator who sends this message to other objects, not the object itself (that was changed). The last pattern was observer. This pattern promotes loose coupling by creating “observable” objects.

These objects then notify all their observers when a specific event occurs. In other words, you can think about it as an object that subscribes (subscriber) to another object’s (publisher) specific activity and gets notified about it. When any event happens, the publisher notifies all its subscribers. Many times, it also sends some message to these subscribers in the form of an event object.

Closing Thoughts on JavaScript Design Patterns

Here you have it! You’ve successfully completed this quick introduction or guide to JavaScript design patterns. Although some of the examples were more difficult or complex than others, I hope you were able to understand how individual patterns work. If you have any problems with understanding anything, let me know and I will reply as soon as possible. Remember, without getting your hands dirty and literally practicing your butt off, all the knowledge you’ve learned through this mini-series will soon disappear. Regular practice is the key for memory retention. Without it, you are wasting your time. If you are interested in getting deeper in JavaScript design patterns, check out great book on this topic by Addy Osmani.

If you liked this article, please subscribe so you don't miss any future post.

If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂

By Alex Devero

I'm Founder/CEO of DEVERO Corporation. Entrepreneur, designer, developer. My mission and MTP is to accelerate the development of humankind through technology.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.