arrow left
Back to Developer Education

    Design Patterns in JavaScript

    Design Patterns in JavaScript

    Developers focus on writing readable, maintainable, and reusable code. A design pattern plays a significant role in this sector. It is a broadly recognized concept in the software design industry due to its benefits. <!--more--> One of the most crucial features of writing maintainable code is identifying recurring structures and functionalities and optimizing them. JavaScript design patterns enable developers to write well-organized and structured code.

    Goal

    This article will help you understand the most popular JavaScript design patterns. They include creation, structural, and behavioral design patterns.

    Table of contents

    Prerequisites

    Design patterns in JavaScript are an exciting and interactive topic. Therefore, the reader should have a thorough understanding of JavaScript and object-oriented programming (OOP).

    What is a design pattern

    A design pattern is a well-defined solution applied to commonly occurring problems in the software engineering industry.

    It is an illustration or a template for problem-solving that can be used in different conditions. Some of the problems solved by design patterns include:

    • Creating classes.
    • Instantiating an object.
    • Interacting with objects.
    • Writing reusable code.

    Some of the benefits of design patterns are highlighted below:

    • They allow one to write reusable code and thus, save time.
    • They help solve common software design problems.
    • Design patterns help to avoid confusion due to well-structured regulations.
    • Design patterns are highly maintainable which reduces overall costs.

    Categories of design patterns

    Design patterns are classified into multiple categories. However, creational, structural, and behavioral design patterns are the most common. We'll discuss these three design patterns.

    Creational design patterns

    These design patterns provide object creation techniques that increase flexibility and reduce complexities.

    Creational design patterns include:

    Structural design patterns

    These patterns focus on the overall architecture including the class structure and composition.

    Structural design patterns organize objects and classes into a larger structure to enhance flexibility and efficiency. They also improve code readability and maintainability.

    The main objective of structural design patterns is to increase the class functionality without changing its composition.

    Structural design patterns include:

    Behavioral design patterns

    Behavioral design patterns enable effective and flexible communication between classes and objects.

    Behavioral design patterns include:

    JavaScript design patterns

    We have learned that there are numerous design patterns. In this part, we will explore and implement the most popular JavaScript design patterns.

    The design patterns will fall under the three categories we discussed above.

    The seven most popular JavaScript patterns are constructor, singleton, prototype, module, decorator, chain responsibility, and command.

    Constructor pattern

    OOP constructors are unique methods that initialize the objects of a class. In JavaScript, nearly everything is an object including functions.

    JavaScript supports object constructors, as demonstrated below:

    //creates a new object
    var firstObject = {};
    //creates a new object
    var secondObject = Object.create( Object.prototype );
    //creates a new object
    var thirdObject = new Object();
    

    Below is an example of a constructor design pattern:

    class Person {  
        constructor(firstName, secondName) {  
            this.firstName = firstName;  
            this.secondName = secondName;  
            this.fullName = function () {  
                console.log(this.firstName + ' ' + this.secondName);  
            };  
        }  
    }  
      
    const Teacher = new Person('Freddy', 'Chris');  
    Teacher.fullName();  //Prints Freddy Chris in the console 
    

    The example above demonstrates a constructor design pattern. We have defined a Person class with firstName and SecondName attributes.

    The fullName() method prints the teacher's first and second name. We have instantiated an object for the class using the new keyword and provided the required attributes.

    Singleton pattern

    Singleton design pattern controls the instantiation of a class to an object. A new class instance is only created when there is no other instance.

    Below is an example of a singleton pattern:

    var singleton = (function() {
        // singleton value which gets initialized only once
        var instance;
    
        function initialization(values){
            this.randomNumber = Math.random();
            values = values || {};
            this.number = values.number || 7;
            this.size = values.size || 12;
        }
        return {
            getValue: function(values) {
                // we initialize the singleton value only once
                if (instance === undefined) {
                    instance = new 
                    initialization(values);
                }
                return instance;
            }
        };
    })();
    
    var insObject = singleton.getValue({ "size": 10 });
    // prints number: 7, size: 10, and a random decimal value
    console.log(insObject);
    var insObject1 = singleton.getValue({ "number": 10 });
    // // prints number: 7, size: 10, and a random decimal value as in first output
    console.log(insObject1);
    

    From the example above, the random number and other values generated are always the same.

    Prototype pattern

    The prototype pattern involves designing objects based on preexisting templates also known as cloning.

    In JavaScript, think of it as prototypical inheritance where objects are created to act as prototypes of other components.

    Below is an example to implement this pattern:

    var schoolSystem = {
        courses: ['Course1', 'Course2'],
        getcourses: function() {
          console.log(this.courses);
        }
      }
      
    var mySchool = Object.create(schoolSystem, 
      { 
        name: {
          writable: true,
          configurable: true,
          value: 'My School'
        }
      });
      
      console.log(mySchool.name); // prints "My School"
      mySchool.getcourses(); // prints ["Course1", "Course2"]
    

    Module pattern

    Modules are small sections of independent and reusable code. In addition, the module pattern helps in code encapsulation.

    Modules create private and public methods. Private functions are properties of a given entity that can only be accessed within a specific class.

    Public methods, on the other hand, can be accessed from any entity.

    Below is an example of implementing the module pattern:

    var countApi = (function(){
      var counter = 0;
    
      var inc = function() {
        counter++;
      }
    
      var dec = function() {
        counter--;
      }
      return {
        increment: function() {
          inc();
        },
        decrement: function() {
          dec();
        },
        reset: function() {
          counter = 0;
        },
        getvalue: function() {
          return counter;
        }
      };
    })();
    
    countApi.increment();
    countApi.increment();
    countApi.increment();
    countApi.decrement();
    countApi.reset();
    console.log(countApi.getvalue());
    

    Decorator pattern

    The decorator design pattern focuses on code reusability which influences an object's behavior. It also incorporates new characteristics and behaviors to predefined classes and objects.

    Let's create a function to assign an object a common functionality or behavior.

    const Doctor = function(type) {
        this.type = type || 'oncologist';
      }
      
      const oncologist = new Doctor('oncologist');
      const dermatologist = new Doctor('dermatologist');
      
      oncologist.oncology = function() {
        console.log('Oncologist is a cancer expert');
        return this;
      }
      
      dermatologist.dermatology = function() {
        console.log('Dematologist is a skin expert');
        return this;
      }
      
      oncologist.oncology();
      dermatologist.dermatology();
      //Prints Oncologist is a cancer expert and Dematologist is a skin expert
    

    Chain of responsibility pattern

    The chain of responsibility pattern mainly works with systems that allow a client's request to pass through a chain of events and is received by handlers. The requests are then processed and passed from one chain to another or rejected.

    A perfect example of this pattern is an ATM. When we withdraw from an ATM, the machine processes the request and dispenses the amount in a series of grouped notes ($200, $100, $50, $20, $5, $1).

    Here is an example of the chain of responsibility pattern:

    const request= function() {
        this.withdraw = function(amount) {
          console.log(`Requesting to withdrawl $${amount.toFixed(2)}`);
      
      
          const dispenseOutput = {};
      
          // chain or responsibility function
          function get(bill) {
            dispenseOutput[bill] = Math.floor(amount / bill);
            amount -= dispenseOutput[bill] * bill;
      
            return { get };
          }
      
          get(200).get(100).get(50).get(20).get(5).get(1);
      
          this.dispense(dispenseOutput);
        };
      
        this.dispense = function(bills) {
          console.log('--- Dispensing cash ---')
          Object.entries(bills).forEach(([key, value]) => {
            console.log(`- Dispensing ${value} $${key} bills`);
          });
        }
      };
      
      const myRequest = new request();
      
      ;
      myRequest.withdraw(1570);
      myRequest.withdraw(250);
    

    In the code above, an object is created requesting a withdrawal amount. This invokes a sequence of calls for the object. Finally, the ATM distributes the combination of notes which satisfies the request.

    Command pattern

    The command design pattern encapsulates actions as objects. Therefore, it is helpful if you wish to decompile or execute objects.

    The command, client, receiver, invoker are participants in this design pattern.

    The example below implements the command design pattern:

    var calc = {
       add: function(a, b) {
           return a + b;
       },
       subtract: function(a, b) {
           return a - b;
       },
       divide: function(a,b){
           return a/b;
       },
       multiply: function (a,b){
           return a*b;
       }
    }
    var arithmetic = {
       compile: function(name, args) {
           if (name in calc) {
               return calc[name].apply(calc, [].slice.call(arguments, 1));
           }
           return false;
       }
    }
    console.log(arithmetic.compile("add", 8, 7)); // prints 15
    console.log(arithmetic.compile("divide", 72, 8)); //prints 9
    console.log(arithmetic.compile("multiply", 4, 3)); // prints 12
    

    Conclusion

    This tutorial went through JavaScript design patterns and how they tackle common and complex problems with ease.

    I hope the article helped you understand the different design patterns in JavaScript.

    Happy Learning!


    Peer Review Contributions by: Wanja Mike

    Published on: Aug 18, 2021
    Updated on: Jul 15, 2024
    CTA

    Start your journey with Cloudzilla

    With Cloudzilla, apps freely roam across a global cloud with unbeatable simplicity and cost efficiency