Skip to content

Design Patterns Javascript: When and how to use

Design Patterns Javascript: When and how to use

Design Patterns Javascript: When and how to usePatterns in Javascript are reusable solutions to common programming problems that have been discovered and refined over time. While design patterns are not specific to any programming language, they can be implemented in any language, including JavaScript.

Let’s start with design patterns Javascript

Here are some of the most commonly used design patterns in JavaScript:

Singleton Pattern

The Singleton pattern restricts the instantiation of a class to a single instance and provides a global point of access to that instance.

var Singleton = (function () {
    var instance;
    function createInstance() {
        // Singleton logic here
        return {};
    }

    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

Module Pattern

The Module pattern is used to encapsulate a set of functions and variables into a single, reusable module. It provides a way to organize code into self-contained units with private and public members.

var Module = (function () {
    var privateVar = "This is a private variable.";

    function privateFunction() {
        console.log("This is a private function.");
    }

    return {
        publicVar: "This is a public variable.",
        publicFunction: function () {
            console.log("This is a public function.");
        }
    };
})();

Observer Pattern

The Observer pattern allows one or more objects to be notified when the state of another object changes. It provides a way for objects to communicate without being tightly coupled.

function Subject() {
    this.observers = [];

    this.addObserver = function (observer) {
        this.observers.push(observer);
    };

    this.removeObserver = function (observer) {
        var index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    };

    this.notifyObservers = function () {
        for (var i = 0; i < this.observers.length; i++) {
            this.observers[i].update();
        }
    };
}

function Observer() {
    this.update = function () {
        console.log("Observer notified.");
    };
}

var subject = new Subject();
var observer = new Observer();
subject.addObserver(observer);
subject.notifyObservers();

Factory Pattern

The Factory pattern is used to create objects without specifying the exact class of object that will be created. It provides a way to encapsulate object creation and decouples the client code from the object creation code.

function ProductA() {
    this.type = "ProductA";
}

function ProductB() {
    this.type = "ProductB";
}

function Factory() {
    this.createProduct = function (type) {
        if (type === "ProductA") {
            return new ProductA();
        } else if (type === "ProductB") {
            return new ProductB();
        }
    };
}

var factory = new Factory();
var productA = factory.createProduct("ProductA");
console.log(productA.type); // "ProductA"
var productB = factory.createProduct("ProductB");
console.log(productB.type); // "ProductB"

Prototype Pattern

The Prototype pattern is used to create new objects by cloning an existing object. It provides a way to create objects without having to know their exact class.

var prototype = {
    type: "prototype",
    clone: function () {
        return Object.create(this);
    }
};

var object1 = prototype.clone();
console.log(object1.type); // "prototype"
var object2 = prototype.clone();
console.log(object2.type); // "prototype"

Decorator Pattern

The Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. It provides a way to add functionality to an object without subclassing.

function Coffee() {
    this.cost = function () {
        return 1;
    };
}

function Milk(coffee) {
    this.cost = function () {
        return coffee.cost() + 0.5;
    };
}

function Sugar(coffee) {
    this.cost = function () {
        return coffee.cost() + 0.2;
    };
}

var coffee = new Coffee();
console.log(coffee.cost()); // 1

coffee = new Milk(coffee);
console.log(coffee.cost()); // 1.5

coffee = new Sugar(coffee);
console.log(coffee.cost()); // 1.7


Command Pattern

The Command pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations. It provides a way to separate the responsibility of issuing commands from the objects that execute them.

function Receiver() {
    this.execute = function () {
        console.log("Receiver executing command.");
    };
}

function Command(receiver) {
    this.execute = function () {
        receiver.execute();
    };
}

var receiver = new Receiver();
var command = new Command(receiver);
command.execute();

Iterator Pattern

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It provides a way to decouple the client code from the object structure.

function Iterator(items) {
    this.index = 0;
    this.items = items;

    this.hasNext = function () {
        return this.index < this.items.length;
    };

    this.next = function () {
        return this.items[this.index++];
    };
}

var items = ["one", 2, "three", "4", "five"];
var iterator = new Iterator(items);
while (iterator.hasNext()) {
    console.log(iterator.next());
}

Facade Pattern

The Facade pattern provides a simplified interface to a larger and more complex system. It provides a way to decouple the client code from the system components.

function Subsystem1() {
    this.operation1 = function () {
        console.log("Subsystem1 operation1.");
    };

    this.operation2 = function () {
        console.log("Subsystem1 operation2.");
    };
}

function Subsystem2() {
    this.operation1 = function () {
        console.log("Subsystem2 operation1.");
    };

    this.operation2 = function () {
        console.log("Subsystem2 operation2.");
    };
}

function Facade() {
    this.subsystem1 = new Subsystem1();
    this.subsystem2 = new Subsystem2();

    this.operation = function () {
        this.subsystem1.operation1();
        this.subsystem2.operation2();
    };
}

var facade = new Facade();
facade.operation();

Proxy Pattern

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. It provides a way to add extra functionality to an object without changing its interface.

function RealSubject() {
    this.request = function () {
        console.log("RealSubject request.");
    };
}

function Proxy() {
    var realSubject = new RealSubject();

    this.request = function () {
        if (this.checkAccess()) {
            realSubject.request();
            this.logRequest();
        } else {
            console.log("Access denied.");
        }
    };

    this.checkAccess = function () {
        // Check access
        return true;
    };

    this.logRequest = function () {
        console.log("Proxy log: request.");
    };
}

var proxy = new Proxy();
proxy.request();

Conclusion

These are just a few examples of the many design patterns that can be implemented in JavaScript. Understanding these patterns and when to use them can help you write cleaner, more maintainable code. Hope you liked the article. 🙂

Further Readings

Inheritance & polymorphism: Organise code better Javascript

Please share