Design patterns are reusable solutions to commonly occurring problems in software design. They are proven solutions, easily reusable and expressive. They lower the size of your codebase, prevent future refactoring, and make your code easier to understand by other developers.

Design patterns help combine experiences of many developers to structure the codes in an optimized manner that meet the problems we are seeking solutions to, and gives common vocabulary used to describe solutions to our problems than describing the syntax and semantics of our code.

JavaScript design patterns assist developers to write organized, beautiful and well-structured codes. Although design patterns, when used can easily be re-used, they can never supplement developers, rather they only support them by preventing minor issues that could lead to major problems on the web application development by providing generalized solutions that are not tied to a specific problem.

In this article I will expose some best and most popular JavaScript design patterns, here’s I will show you Categories of Design Patterns

Categories of Design Patterns

Design patterns are divided into many categories, but the most common are Creational, Structural and Behavioral, here’s a quick overview!

The constructor pattern

In JavaScript almost everything is an object, we have most often interested in object constructors. Since object constructors are used to creating specific types of objects, for example, both preparing the object for use and accepting arguments a constructor can use to set the values of member properties and methods when the object is first created

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}

// Usage:
var honda = new Car('TRX', '2020', '1000');

The module pattern

Modules are an integral piece of any robust application’s architecture and typically help in keeping the unit of code for a project cleanly separated and organized
There are several option for implementing modules. These include:

  • Object literal notation
  • The module Pattern
  • AMD modules
  • CommonJS module
  • ECMAScript Harmony modules
var testModule = (function() {
var counter = 0;
return {
incrementCounter: function() {
return ++counter;
},
resetCounter: function() {
counter = 0;
}
};
})();

// Usage:
testModule.incrementCounter();
testModule.resetCounter();

The Revealing Module Pattern

One thing that the revealing module can do is avoiding repeat the name of the main object when we want to call one public method from another or access public variables.

var myRevealingModule = (function() {
var privateVariable = 'not okay',
publicVariable = 'okay';
function privateFun() {
return privateVariable;
}

function publicSetName(strName) {
privateVariable = strName;
}

function publicGetName() {
privateFun();
}

return {
setName: publicSetName,
message: publicVariable,
getName: publicGetName
};
})();

//Usage:

myRevealingModule.setName('Naufal');

The Singleton Pattern

The singleton design pattern lets us create no more than a single instance of a class. It is commonly used for creating database connections, setting up a logger for an application. The configuration-related stuff should execute only once and should be reused until the application is live.

let dbInstance = null

function getDBConn () {
if (!dbInstance) {
dbInstance = new DB() // Creating an instance of DB class and storing it in the global variable dbInstance
}
return dbInstance
}

function useDBConn () {
let dbObj = getDBConn()
/* --- */
}

function f1 () {
let dbObj = getDBConn()
/* --- */
}

function f2 () {
let dbObj = getDBConn()
/* --- */
}

Observer Pattern

Observer pattern is a handy place where we can communicate with other object simultaneously, there is no unnecessary push and pull of events across the states, rather the modules involved only modify the current state of data

function Observer() {
this.observerContainer = [];
}

Observer.prototype.subscribe = function (element) {
this.observerContainer.push(element);
}

// the following removes an element from the container

Observer.prototype.unsubscribe = function (element) {

const elementIndex = this.observerContainer.indexOf(element);
if (elementIndex > -1) {
this.observerContainer.splice(elementIndex, 1);
}
}

/**
* we notify elements added to the container by calling
* each subscribed components added to our container
*/
Observer.prototype.notifyAll = function (element) {
this.observerContainer.forEach(function (observerElement) {
observerElement(element);
});
}

The Prototype Pattern

One of the benefits of using the Prototype pattern is that we’ve working with the prototype strengths JavaScript has to offer natively rather than attempting to imitate features of other languages

var myCar = {
name: 'honda',
drive: function() {
console.log('I am driving!');
},
panic: function() {
console.log('wait, how do you stop this thing?');
}
};

//Usages:

var yourCar = Object.create(myCar);

console.log(yourCar.name); //'honda'

The Factory Pattern

Factory can provide a generic interface for creating objects, where we can specify the type of factory object we wish to create.

function Car(options) {
this.doors = options.doors || 4;
this.state = options.state || 'second';
this.color = options.color || 'blue';
}

The Mixin Pattern

One of my favorite JS design pattern, Mixins are classes that offer functionality that can be easily inherited by a sub-class or group of sub-classes for the purpose of the function reuse

var Person = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = 'male';
};

var clark = new Person('Naufal', 'RDJ');

var Superhero = function(firstName, lastName, powers) {
Person.call(this.firstName, this.lastName);
this.powers = powers;
};

SuperHero.prototype = Object.create(Person.prototype);
var superman = new Superhero('Clark', 'Kent', ['flight', 'heat-vision']);

console.log(superman);

This might be not the most detail and clear pattern. All the patterns may not use to one project, and some projects may benefit from the decoupling benefits offered by the Observer pattern. That said, once we have a firm grasp-of design patterns and the specific problems they are best suited to. Thus, it becomes much easier to integrate into our application architecture.

In my conclusion, the more complex the problem, the more it makes sense to modularize it. Therefore, the initial reasoning in OOP programming, far from being complex, simplifies the complex. Even the most daunting programming problem can be solved with Design Pattern

References :

Software Engineer who got D score in Introduction to Programming at college 👨‍🎓🧑‍💻