Table of Contents
Writing a clean and maintainable code is not an easy task. Regardless of the size of our projects, we often find ourselves dealing with almost pure chaos. It is not a wonder that many of us want to know how to write clean and maintainable code. Fortunately, in this post you will explore one way to achieve this. Have you ever heard about JavaScript design patterns? This series will be a great introduction into this subject. You will explore the theory behind and then practice it on examples. Are your ready to take your programming skills on another level?
Last 5 patterns, and summary, are in part 2.
Why Should You Use Design Patterns
Using JavaScript design patterns sound pretty cool. It can also impress your colleagues and nerdy friends. However, why should you think about diving deeper into this abstract subject? How can JavaScript design patterns make your work easier or better? Both of these questions are great and you should demand answers on them. The first and foremost reason to explore, try and learn various design patterns is that it will help you keep your code cleaner, more organized and maintainable. As you can guess from the name, various JavaScript design patterns describe different ways to design and structure your code. And, not only that.
Every design pattern also contains specific principles and even the best practices for its implementation. Before you will start thinking about these patterns like about a stack of more or less same tools or approaches, you should know one thing. JavaScript design patterns are not all the same. All of them have a specific purpose or way of use. Meaning, every, or almost every, pattern is meant to be used in specific situation or scenario. However, that doesn’t mean you can’t stick with one and use it the majority of your project. If you will find any pattern interesting enough to implement it into your style, go ahead.
Remember, just because someone is saying that you shouldn’t or can’t use fork for eating seeds doesn’t mean you have to follow that advice. You can do whatever you want if you are willing to also take the responsibility for it. Anyway, back to the JavaScript design patterns. So, although there is a specific situation for every pattern, feel free to break any if these suggestions.
As said, the main purpose of JavaScript design patterns is to make your code cleaner and more maintainable. This is done by imposing a set of specific rules on the way you write your code. Every pattern basically forces you to follow some structure. So, when you adopt any of these patterns, the majority of your syntax will be built in the same way. The only things that will be different will be content and functionality. Using design patterns will also make your code more modular and less prone to become cluttered. This will result in smaller need to refactoring it. Also, imposing a design pattern on your code will make it easier for you to understand it when you will come back to it after some time.
By the way, if you will not find any of the patterns we will explore today interesting, you can strike your own path. By following the best practices, you can create your own pattern and also make it public so other people can use it as well. However, because we already covered this subject in previous, more theoretical, post about JavaScript design patterns we are not going to go back again. If you are interested in creating your own pattern, here are the information you need.
Exploring JavaScript Design Patterns
Singleton
The first design pattern we will start with is pattern called Singleton. The idea behind this pattern is that you have only one instance of a specific class. So, when you will use this same class for the second time to create a new object, you should get the same object just like at the first time this class was used. As you might know, there are no classes in JavaScript. Well, let me rephrase that. There are no classes in old JavaScript. In ECMAScript 6, however, classes were introduced.
However, they are not a completely new concept. Even in ES 6 they are still build on the same old prototype-based inheritance. What’s new is simpler syntax to create new objects.
Anyway, back to the singleton. Interesting fact about JavaScript – no two objects are created equal or alike. Meaning, every time when you create new object in JavaScript that is identical with one already existing, they are not the same. Conclusion? Any time you will create new object using object literal, you are already using a singleton pattern. Let’s illustrate this on a simple example.
// The first object var person1 = { firstName: “Kurt”, lastName: “Gödel”, superpowers: [“mathematic”, “logic”, “philosophy”], nationality: “Austrian” }; // The clone of the first object var person2= { firstName: “Kurt”, lastName: “Gödel”, superpowers: [“mathematic”, “logic”, “philosophy”], nationality: “Austrian” }; // Test to prove our assumption person1 === person2; // false person1 == person2; // false
That being said, what will happen if you create an object using constructor function? Well, the first option is to cache the instance in a property of your constructor function. The downside of this approach as you will see on the example below is that our instance is public and can be changed.
function Person() { // if we have an existing instance if (typeof Person.instance === “object”) { return Person.instance; } // else this.hasName = true; this.species = “Human”; this.numOfHands = 2; this.numOfLegs = 2; // Caching instance Person.instance = this; } // Test to prove our assumption var godel = new Person(); var frege = new Person(); godel === frege; // true
Another interesting solution that doesn’t include exposing public instance is to create our constructor and then wrap it in self-invoking function. Now, when the constructor is invoked for the first time, it will create an object. And, it will also point the private instance to it. Then, when the constructor is invoked for the second time and so on, it will return the private variable.
var Person; (function() { var instance; Person = function() { // test for existing instance if (instance) { return instance; } // Couple properties this.hasName = true; this.species = “Human”; this.numOfHands = 2; this.numOfLegs = 2; // Caching instance instance = this; } })(); // Test to prove our assumption var godel = new Person(); var frege = new Person(); godel === frege; // true
That’s it for singleton. Next one on the list of JavaScript design patterns is Factory.
Factory
The idea of factory pattern is to create objects and perform similar operations while doing it, hence the name “factory”. Objects created by factory pattern (or method) are inheriting their properties from the same parent object. The key objective of the factory is keeping your code extensible. Let’s demonstrate how factory works by using a simple example.
// Factory constructor function Factory() { // Function for creating individual products this.createProduct = function(type) { // Variable for product var product; // Defining specific products according to their type if (type === “Phone”) { product = new Phone(); } else if (type === “Smartphone”) { product = new Smartphone(); } else if (type === “Tablet”) { product = new Tablet(); } else if (type === “Notebook”) { product = new Notebook(); } else if (type === “Desktop”) { product = new Desktop(); } // Setting type of product variable to “type” so we can use it to specify the product product.type = type; // Function to return time it will take to build specific product product.info = function () { return this.type + “ will be build in “ + this.hours + “ hours.”; }; // Making “product” variable accessible return product; }; } // Defining specific products with custom construction time var Phone = function() { this.hours = 4; }; var Smartphone = function() { this.hours = 8; }; var Tablet = function() { this.hours = 21; }; var Notebook = function() { this.hours = 23; }; var Desktop = function() { this.hours = 31; }; // Function to test the factory method and build the products function build() { // Array to store new products var products = []; // New instance of Factory object var factory = new Factory(); // Populating products array with individual products products.push(factory.createProduct("Phone")); products.push(factory.createProduct("Smartphone")); products.push(factory.createProduct("Tablet")); products.push(factory.createProduct("Notebook")); products.push(factory.createProduct("Desktop")); // Printing info about construction time of individual products for (var i = 0, j = products.length; i < j; i++) { console.log(products[i].info()); } } build();
In the JavaScript example above the Factory object creates five different types of products. Each of these products has a specific type with different construction time. One thing to point out is that the “createProduct” method is the actual factory method. In the simple terms, we instruct the factory what type of product we want to create by passing a type argument into the factory method. Then, four different types of products are created and stored in “products” array. As the last step, each product gives as the amount of time it will take to build it.
The last thing to mention is that factory is one of the most used JavaScript design patterns. Now, let’s move to third pattern called iterator.
Iterator
The best use of Iterator pattern is when you need or want to provide a way to access the data of some collector object without necessarily exposing these data or their structure. In other words, you or someone else wants to be able to iterate through and read the data inside an object even though you have no idea about their structure.
// Creating Iterator method; we will call it Storage var Storage = function (items) { this.index = 0; this.items = items; }; // Adding new methods to Storage prototype Storage.prototype = { // Method for loading the first element in array first: function() { this.reset(); return this.next(); }, // Method for iterating to next element in array next: function() { return this.items[this.index++]; }, // Method for checking whether are we on the end of array hasNext: function() { return this.index <= this.items.length; }, // Method for reseting the index reset: function() { this.index = 0; } } function distribute() { // Creating array with items to loop through var items = [“Kant”, true, 13, “human”]; // Creating new instance of Storage object var storage = new Storage(items); // Using for loop to loop through storage object for (var item = storage.first(); storage.hasNext(); item = storage.next()) { console.log(item); } // Or, using while loop to loop through storage object while (storage.hasNext()) { console.log(storage.next()); } } // Initiating the distribute function distribute();
The Iterator object we used, in our example called Storage, maintains a reference to the collection and the current index (or position). Next, we implemented couple methods called “first”, “next”, “hasNext” and “reset. Then, we used two possible options to loop through “items” array created by “distribute” function and provided to new instance of Storage. These loops are for and while. The for loop utilizes the “first”, “hasNext” and “next” methods to control the iteration.
The next member of JavaScript design patterns family waiting for exploration is Decorator.
Decorator
The purpose of decorator pattern is to add additional functionality (decorate) to object’s behavior dynamically. Decorator object adds new behavior by wrapping itself around the original object. You can imagine it like adding another layer on top of the object. I should also mention that multiple decorators can add functionality to the original object or even override one already existing. The main reason for choosing decorator amongst other JavaScript design patterns is its ability of customization and configuration. Meaning, you start off with plain object containing only basic functionality. Then, you use available “decorators” to enhance it.
// Creating new object called Vehicle function Vehicle(type) { this.type = type || “car”; this.model = “default”; } var testVehicle = new Vehicle(); // Testing testVehicle instance console.log(testVehicle); // model: “default”, type: “car” // Let's create a new instance of Vehicle object var suv = new Vehicle(“SUV”); // Next, we will decorate the “suv” object with new functionality suv.setNumOfWheels = function(num) { this.numOfWheels = num; }; suv.setPrice = function(amount) { this.price = “$” + amount; }; suv.setColorVariant = function(color) { this.colorVariant = color; }; // Test the decorators suv.setNumOfWheels(4); suv.setPrice(250000); suv.setColorVariant(“Yellow”); // Testing the “suv” instance console.log(suv); // colorVariant: “Yellow”, model: “default”, numOfWheels: 4, price: “$250000”, type: “SUV” console.log(testVehicle); // still model: “default”, type: “car”
To summarize what we’ve done in the example above … We created basic object called “Vehicle” with two variables inside. Next, we created an instance of “Vehicle” object to test its functionality. Then, we created another instance of “Vehicle” called “suv” and decorated it with couple methods: “setNumOfWheels”, “setPrice” and “setColorVariant”. Lastly, we tested these decorators and also both instances of “Vehicle” – the “suv” and “testVehicle” objects.
Closing Thoughts on JavaScript Design Patterns
This is it for this first part of JavaScript design patterns series. Today, we’ve covered the first four JavaScript design patterns. I hope you enjoyed this tutorial and found it useful. In the next part you will learn about patterns such as strategy, facade, proxy and much more. Until then, practice what you’ve learned today and stay tuned for the sequel.
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 🙂