Sign in
Log inSign up
Design-Patterns By JavaScript

Photo by Karl Solano on Unsplash

Design-Patterns By JavaScript

This article discusses some of the behavioral patterns of using JavaScript

MohammadReza Khorrami's photo
MohammadReza Khorrami
·Apr 8, 2022·

6 min read

We have many kind types of design-patterns, one is structural design-pattern, which allowes us to communicate well between classes and methods.

The second type of creational This type of solution provides us with solutioms to build a function method class or interface or whatever.

The third type of behavioral, which deals only with the behavior of an entity, means classes of methods, interfaces, calles entities.

The next design pattern is a concurrent pattern that deals with how entities run simultaneously.

Creational patterns include factory, builder, lazy instantiation, mutation, prototype, singleton. Structural patterns including adapter, bridge, decorator, facade, proxy. Behavioral patterns include chain of responsibility, command, interpreter, iterator, mediator, memento, observer, strategy, template method, visitor.

This article only covers some of these patterns and it is very important that you can say the names of these patterns during the interview.

Creational Patterns

Factory Pattern

Defines an interface for constructing objects while allowing them to decide what object to model. In fact, this template allows prototyping to be assigned to subclasses.

class Developer
{
    constructor(name, type) {
        this.name = name;
        this.type = type;
    }
}

class Tester
{
    constructor(name, type) {
        this.name = name;
        this.type = type;
    }
}

class EmployeeFactory
{
    static create(name, type) {
        switch(type) {
            case 'developer':
                return new Developer(name, type)
            case 'tester':
                return new Tester(name, type)
        }
    }
}

const employees = [];

employees.push(EmployeeFactory.create('John', 'developer'));
employees.push(EmployeeFactory.create('Jain', 'developer'));
employees.push(EmployeeFactory.create('Sara', 'tester'));
employees.push(EmployeeFactory.create('Joe', 'tester'));

for(const employee of employees) {
    console.log(employee);
}

As you can see in the example above, we have two classes called developer and tester and a subclass from which we created a factory called EmployeeFactory from the above two classes, which is responsible for the instantioate work of the two upper classes.

The above example illustrates well the definition of a factory pattern.

Builder Pattern

The process of making an object separate from its display so that the same manufacturing process can be used to create different views.

class Shop
{
    build(builder) {
        builder.step1();
        builder.step2();
        return builder.get();
    }
}

class CarBuilder
{
    constructor() {
        this.car = null;
    }

    step1() {
        this.car = new Car();
    }

    step2() {
        this.car.addParts();
    }

    get() {
        return this.car;
    }
}

class TruckBuilder
{
    constructor() {
        this.truck = null;
    }

    step1() {
        this.truck = new Truck();
    }

    step2() {
        this.truck.addParts();
    }

    get() {
        return this.truck;
    }
}

class Car
{
    constructor() {
        this.doors = 0;
    }

    addParts() {
        this.doors = 4;
    }

    say() {
        console.log(`I have ${this.doors} doors`);
    }
}

class Truck
{
    constructor() {
        this.doors = 0;
    }

    addParts() {
        this.doors = 2;
    }

    say() {
        console.log(`I have ${this.doors} doors`);
    }
}


const shop = new Shop();
const carBuilder = new CarBuilder();
const truckBuilder = new TruckBuilder();
const car = shop.build(carBuilder);
const truck = shop.build(truckBuilder);

car.say();
truck.say();

In the example above we have a class called shop which is a builder and builds two classes CarBuilder, TruckBuilder for us and also two car and truck classes which call the functions defined in CarBuilder and TruckBuilder classes and finally we have shop classes. CarBuilder, TruckBuilder Once we make a sample and in two samples car and truck we use and make a sound.

The above example illustrates well the definition of a builder pattern.

Structural Pattern

Adapter Pattern

The interface of one class turns into another interface that a client expects. This pattern allows cooperation between objects that previously could not work together due to incompatible relationships.

// OLD INTERFACE
class Shipping
{
    request(zipStart, zipEnd, weight) {
        // ...
        return "$50.00";
    }
}

// NEW INTERFACE
class AdvancedShipping
{
    login(credentials) {
        // ...
    }

    setStart(start) {
        // ...
    }

    setDestination(destination) {
        // ...
    }

    calculate(weight) {
        return "$80.00";
    }
}

class ShippingAdapter
{
    constructor(credentials) {
        this.credentials = credentials;
        this.shipping = new AdvancedShipping();
        this.shipping.login(this.credentials);
    }

    request(zipStart, zipEnd, weight) {
        this.shipping.setStart(zipStart);
        this.shipping.setDestination(zipEnd);
        return this.shipping.calculate(weight);
    }
}


const shipping = new Shipping();
const credentials = {token: "asdasd" };
const adapter = new ShippingAdapter(credentials);

let cost = shipping.request("78712", "10012", "2kg");
console.log('Old Cost: ' + cost);


cost = adapter.request("78712", "10012", "2kg");
console.log('New Cost: ' + cost);

As you can see in the example above, we have a shipping class and AdvancedShipping that do not have a request method from the shipping class inside the AdvancedShipping class, so we have to use the ShippingAdapter class as an adapter to use the dose request method inside AdvancedShipping.

Then we create an instance of the shipping class and a variable called credentials and also an instance of the ShippingAdapter class and pass the above variable to this instance, What was used before was shipping.request and what we are using now is adapter.request.

The above example illustrates well the definition of a adapter pattern.

Bridge Patterns

Separate a single concept from its implementation so that both can change independently.

class Gesture
{
    constructor(output) {
        this.output = output;
    }

    tap() {
        this.output.click();
    }

    swipe() {
        this.output.move();
    }

    pan() {
        this.output.drag();
    }

    pinch() {
        this.output.zoom();
    }
}

class Mouse
{
    constructor(output) {
        this.output = output;
    }

    click() {
        this.output.click();
    }

    move() {
        this.output.move();
    }

    down() {
        this.output.drag();
    }

    wheel() {
        this.output.zoom();
    }
}

class Screen
{
    click() {
        console.log('Screen clicked');
    }

    move() {
        console.log('Screen moved');
    }

    drag() {
        console.log('Screen dragged');
    }

    zoom() {
        console.log('Screen zoomed');
    }
}

class Audio
{
    click() {
        console.log('Audio clicked');
    }

    move() {
        console.log('Audio waved');
    }

    drag() {
        console.log('Audio volume down');
    }

    zoom() {
        console.log('Audio volume up');
    }
}


const screen = new Screen();
const audio = new Audio();

const hand = new Gesture(screen);
const mouse = new Mouse(audio);

hand.tap();
hand.swipe();
hand.pinch();

mouse.click();
mouse.move();
mouse.down();

As you can see above, we have two input classes called Gesture and Mouse and two output classes called Screen and Audio. Finally, we create an example of the screen and audio classes and use the gesture and mouse classes as a bridge and put the screeen and audio examples inside.

The above example illustrates well the definition of a bridge pattern.

Behavioral patterns

Iterator Pattern

Provides a way to sequentially access elements of an integrated (composite) object without disclosing its representation.

class Iterator
{
    constructor(items) {
        this.index = 0;
        this.items = items;
    }

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

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

const items = [1, 'John', false, 2.25];
const iterator = new Iterator(items);

while (iterator.hasNext()) {
    console.log(iterator.next());
}

As you can see in the example above we created an iterator class that takes the items which has two methods next for the next item and hasnext to check if there is an item or not and finally we create an example of this class and the items Show, This example is very similar to the generator method in JavaScript.

The above example illustrates well the definition of a iterator pattern.

Strategy Pattern

Defines a family of algorithms that can be encapsulated and replaced, a strategy that allows an algorithm to be modified regardless of where it is used.

class UPS
{
    constructor() {
        this.name = 'UPS';
    }

    calculate() {
        return 25;
    }
}

class FedEx
{
    constructor() {
        this.name = 'FedEx';
    }

    calculate() {
        return 30;
    }
}

class Shipping
{
    setStrategy(company) {
        this.company = company;
    }

    calculate() {
        return this.company.calculate();
    }
}


// const package = { from: "76712", to: "10012", weight: "1kg" };

const shipping = new Shipping();

shipping.setStrategy(new UPS());
console.log("UPS Strategy: " + shipping.calculate());

shipping.setStrategy(new FedEx());
console.log("Fedex Strategy: " + shipping.calculate());

As you can see in the example above, we have three classes, UPS, Fedex, and shipping. The shipping class calls the calculate method, which exists in the two classes above. Finally, we sample the shipping class and use the setStrategy method, the UPS and Fedex classes. We do and in principle these two classes are not related to the shipping class, but this class takes care of the modified work.

The above example illustrates well the definition of a strategy pattern.

I hope this article has helped you to better understand some important examples of patterns. Thank you for your support.