Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 14, 2021 01:47 pm GMT

Using Design Patterns in Node.js

This article was first published at my Blog here

Using Design Patterns in Node.js Programming

When we code or create programs, we always want our written code to be adaptable, optimized and reusable. And often while building software's, we want that - changes in our code should not affect the other features or create bugs, or the impact of the change to be low.

Some say, coding is like a art - you can be creative. Yes, certainly but when it comes to building real-world software's, our code should always follow a design / technique that helps in solving the common problems while we code. Design patterns help in writing better, maintainable software which minimizes the impact, and also makes your code look beautiful .For those who believe in practicing quality code and engineering ethics, you should definitely checkout and follow Design Patterns !

So In, this article , lets understand all the design patterns that you can use in Nodejs.

Tip : Recommended Books to Read on Design Patterns:

  • Head First Design Patterns
  • Design Patterns : Elements of Reusable Object Oriented Software

Types of Design Patterns

There are about 26 patterns. These 26 can be classified in to 3 types:

  1. Creational Patterns
  2. Structural Patterns
  3. Behavioral Patterns

Creational Patterns

A creational design pattern basically solves a problem by controlling the creation process of an object. There are 4 creational design patterns :

  1. Singleton
  2. Prototype
  3. Factory
  4. Builder

Singleton Design Pattern

Ensure a class only has one instance, and provide a global point of access to it. Sometimes you need to make sure that you have one and only one instance of an object. This is where singleton pattern can be useful.
The singleton represents a single instance of an object.

eg.
Suppose we want to create our own Logger, that will log all our project messages. Usually creating a logger and calling it every other file creating new instances will coz for multiple instantiation of same class. But using singleton we can solve this problem by instantiating only 1 time, and reusing it everywhere :

Let's create a logger singleton instance :
Logger.js

class Logger {  constructor(){      // check if instance is created already      if(Logger.instance == null){       // no instance created yet          this.logs=[];          Logger.instance = this;       }       return Logger.instance;   }   // save our logs to output   log(message){     this.logs.push(message);     console.log(`Log => ${message}`);   }   // count for logs    printLogCount(){      console.log(`${this.logs.length}`);    }} // Actual instance of singleton const logger = new Logger();module.exports = logger;

Now we are ready to use our "logger" singleton anywhere in our application, all we need to do is call the logger.

app.js

const logger = require('./Logger');const logMe = () => {  logger.printLogCount();  logger.log("Hello World ... ");  logger.printLogCount();}

Prototype Design Pattern

Javascript is a Prototype-based language. We specify the kinds of objects to create using prototypical instance (blueprints) and create new objects by copying this prototype. It does this through what's called prototypal inheritance.

An object that supports cloning is called a prototype. Using the prototype pattern, we can instantiate new objects based on a template of an existing object through cloning. When object creation is time consuming and costly operation, so we create object with existing object itself.

One significant benefit of using this design pattern is that functions defined in objects are created by reference. That means all objects point to the same function instead of having their copies of that function. In simpler terms, the prototype's functions are available to all the objects inherited from the prototype.

  • A function defined on the prototype is inherited by all new classes.
  • The new classes point to the same function, rather than having individual copies.

eg.
Suppose we want to create cars objects , lets define a car prototype or(blueprint of a car)

function Car(make, model, year) {   function constructor(make, model, year){     this.make = make;     this.model = model;     this.year = year;   }  let instance = new constructor(make, model, year);  return instance;}

We can create new cars using the above prototype:

const ATV = Car( 'Honda', 'Rincon 650', '2018');

We can also create new objects by cloning the prototype using "Object.create" feature defined by ES5 standard.

// Prototype classconst car = {    noOfWheels: 4,    start() {        return 'started';    },    stop(){        return 'stopped';    },}// using Object.create to create clones const myNewCar = Object.create(car, {owner: {values: 'John'}});console.log(myNewCar.__proto__ === car); //true

Factory Design Pattern

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses.

Instead of using a class constructors or new keyword to create an object of a class, we can abstract this process.

  • Factory pattern provides an interface/abstract class for creating objects.
  • You can create different objects by using the same interface/abstract class.
  • It improves the structure of the code and makes it easier to maintain it.

eg.
Suppose we want to create a shape factory that will return different shapes object like rectangle, square, circle or triangle and its associated function, by passing the name of that shape.

// Factory method for creating new shap instancesfunction shapeFactory(){   this.createShape = function(shapeType){      var shape;      switch(shapeType){         case "rectangle":             shape = new Rectangle();             break;         case "square":             shape = new Square();             break;          case "circle":              shape = new Circle();              break;           case "triangle":               triangle = new Triangle();               break;            default:                 shape = new Rectangle();                break;       }       return shape;    }}// Constructor for defining new Rectanglevar Rectangle = function () {    this.draw = function () {        console.log('This is a Rectangle');    }};// Constructor for defining new Squarevar Square = function () {    this.draw = function () {        console.log('This is a Square');    }};// Constructor for defining new Circlevar Circle= function () {    this.draw = function () {        console.log('This is a Circle);    }};// Constructor for defining new Trianglevar Triangle = function(){    this.draw = function () {        console.log('This is a Triangle);    }}var factory = new shapeFactory();// Creating instance of factory that makes rectange, square, circle, triangle respectivelyvar rectangle = factory.createShape('rectangle');var square = factory.createShape('square');var circle = factory.createShape('circle');var triangle = factory.createShape('triangle');rectangle.draw();square.draw();circle.draw();triangle.draw();/*  OUTPUT  This is a Rectangle  This is a Square  This is a Circle*/

Lets take another example of Vehicle,

If we want to create vehicles, a can be several different types and categories. Suppose we have cars and bikes - that comes under the category of Motorvehicle, and similarly we have airplane that comes under AircraftCategory and also trains in Railvehicle category.

Let's create this category classes,

Motorvehicle.js

// motorvehicle factory function Motorvehicle(){    createVehicle(type, make, model, year){        var vehicle;    switch(type){        case "car":            vehicle = new Car(make, model, year);            break;        case "bike":            vehicle = new Bike(make, model, year);            break;        default :            vehicle = new Car(make, model, year);            break;    }    return vehicle;    }}// constructor for defining new car class Car {    constructor(make, model, year){        this.type="Car";        this.make = make;        this.model = model;        this.year = year;    }}// constructor for defining new bikeclass Bike {        constructor(make, model, year){        this.type="Bike";        this.make = make;        this.model = model;        this.year = year;    }}module.exports = Motorvehicle;

Similarly we can create for Aircraft and Railvehicle category factories

// importing the vehicle categories factoriesimport Motorvehicle from './Motorvehicle';import Aircraft from './Aircraft';import Railvehicle from './Railvehicle';// factory to create a new vehicle based on the vehicle type and detailsfunction VehicleFactory (type, make, model, year) {        this.createNewVehicle = function(type, make, model, year){        var vehicle;        if(type === "car"){                return new Motorvehicle.createVehicle('car', make, model, year);        }        else if(type === "bike"){                return new Motorvehicle.createVehicle('bike', make, model, year);        }        else if(type === "airplane"){                return new Aircraft.createAircraft('airplane', make, model, year);        }else{          return new Railvehicle.createRailVehicle('train', make, model, year);       }    }}module.exports = VehicleFactory;

Now let's create our vehicle

createVehicle.js

 import VehicleFactory from './vehiclefactory'; var Audi101 = VehicleFactory.createNewVehicle('car', 'Audi', '101 All Road', '2021'); // now Audi101 is a car  console.log('Audi101', Audi101);  

Builder Design Pattern

As a programmer creating objects is one of the most common things that you will do. It is so common that many of us never think twice about how we do it, but this can lead to incredibly messy code. This is where the builder pattern comes in. The builder pattern is one of the best creational design patterns for creating complex objects without complicating your constructors or code. The best part about the builder pattern is that the new changes to JavaScript allow us to create extremely concise builders compared to the traditional way of creating builders.

The Builder design pattern allows us to seperate the construction of objects from their representation. Thus it simplifies the code that creates complex objects.

eg.
Let say we want to build cars for an car inventory. In this example, we would have the Car and CarBuilder classes supposing isForSale and isInStock are our optional parameters.

Car.js

class Car{    constructor(make, model, year, isForSale = true, isInStock = false){        this.make = make;        this.model = model;        this.year = year;        this.isForSale = isForSale;        this.isInStock = isInStock;    }    toString() {        return console.log(JSON.stringify(this));    }}module.exports = Car;

Now let's create our CarBuilder

import Car from './Car';class CarBuilder{    constructor(make, model, year){        this.make = make;        this.model= model;        this.year= year;    }    notForSale(){  // to set value of isForSale        this.isForSale = false;        return this;    }    addInStock(){ // to set value of isInStock        this.isInStock = true;        return this;    }    build(){ // build the car        return new Car(this.make, this.model, this.year, this.isForSale, this.isInStock);    }}module.exports= CarBuilder;

Now we can create cars using the CarBuilder class instead of the Car class. We do it as follows:

const CarBuilder = require('./CarBuilder');const bmw = new CarBuilder('bmw', 'x6', 2020).addInStock().build();const audi = new CarBuilder('audi', 'a8', 2021).notForSale().build();const mercedes = new CarBuilder('mercedez-benz', 'c-class', 2019).build();

As you might have noticed, we used the functions notForSale and addInStock, to declare the optional parameters like isForSale and isInstock, which really is very easy to understand and reduces complexity.If dont pass them, by default it applies that isForSale to be true and isInStock = false.

Using the Builder design pattern makes the creation of complex objects less prone to errors because you can easily understand what each parameter does. For contrast, here is how we would create cars without the CarBuilder class:

const bmw = new CarBuilder('bmw', 'x6', 2020, true, true);

You can see it can become confusing. What do those two boolean values ("true") mean? Now imagine the object is more complex; creating the object would be confusing, and the chances of introducing errors would be higher.

Thus, the Builder design pattern is useful to separate the creation and representation of complex objects.

Luckily, in javascript, we can also build patterns also like this using the empty object as optional parameters in the constructor:

class Car {    constructor(make,model, year, {isInStock=false, isForSale=true} = {}) {    this.make = make;        this.model = model;        this.year = year;        this.isForSale = isForSale;        this.isInStock = isInStock;   }}let car = new Car('mercedez-benz', 'c-class', 2019, {isInStock=true, isForSale=true});console.log('car', car);

So this were the Creational Design Patterns, next article we gonna check at Structural Design Patterns.

Peace out !


Original Link: https://dev.to/sujaykundu777/using-design-patterns-in-node-js-58oj

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To