Skip to main content

Design Patterns

Singleton Pattern

class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
this.data = {
name: "Pawan",
age: 25,
};
Singleton.instance = this;
}

getData = () => this.data;
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); //true
console.log(instance1.getData()); //{ name: 'Pawan', age: 25 }
console.log(instance1.getData()); //{ name: 'Pawan', age: 25 }

Factory Pattern

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayGreeting = () => {
console.log(`Hello , this is ${this.name} and I am ${this.age} years old`);
};
}

class PersonFactory {
static createPerson(name, age) {
return new Person(name, age);
}
}

const p1 = PersonFactory.createPerson("Pawan", 25);
const p2 = PersonFactory.createPerson("Ayush", 26);

p1.sayGreeting(); //Hello , this is Pawan and I am 25 years old
p2.sayGreeting(); //Hello , this is Ayush and I am 26 years old

Module Pattern

class MyModule {
#privateChar = "I'm private";

#getPrivateChar = () => {
console.log("I'm private method");
};

publicMethod = () => {
console.log("I'm public method");
};
}

const mm = new MyModule();

mm.publicMethod(); //I'm public method
// mm.#getPrivateChar(); //Property '#getPrivateChar' is not accessible outside class 'MyModule' because it has a private identifier.
// mm.#privateChar; //Property '#privateChar' is not accessible outside class 'MyModule' because it has a private identifier.

Proxy Pattern

const person = {
name: "pawan",
age: 25,
};

const handler = {
get: (target, key) => {
if (key === "name") return target[key].toUpperCase();
},
set: (target, key, value) => {
if (key === "address") {
key = key + "01";
}
target[key] = value;
},
};

const proxy = new Proxy(person, handler);

proxy["address"] = "Lucknow";
console.log(proxy); //{ name: 'pawan', age: 25, address01: 'Lucknow' }

Builder Pattern

class Product {
constructor(name, price, category) {
this.name = name;
this.price = price;
this.category = category;
}
}

class ProductBuilder {
constructor(name) {
this.product = new Product();
this.product.name = name;
}

withPrice = (price) => {
this.product.price = price;
return this;
};

isCategory = (category) => {
this.product.category = category;
return this;
};

build = () => {
return this;
};
}

const product = new ProductBuilder("Widget")
.withPrice(19.99)
.isCategory("Gadgets")
.build();

console.log(product.product);

CircuitBreaker Pattern

class CircuitBreaker {
constructor(threshold, timeout) {
this.threshold = threshold; // number of failures before tripping the circuit
this.timeout = timeout; // time to wait before attempting to call the service again
this.failureCount = 0; // number of consecutive failures
this.lastFailureTime = null; // time of the last failure
this.isCircuitOpen = false; // whether the circuit is currently open
}

async callService() {
if (this.isCircuitOpen) {
const now = Date.now();
if (now - this.lastFailureTime > this.timeout) {
this.isCircuitOpen = false;
this.failureCount = 0;
} else {
throw new Error("Circuit is open");
}
}

try {
// Call the service here
const result = await someServiceCall();
this.failureCount = 0;
return result;
} catch (error) {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.threshold) {
this.isCircuitOpen = true;
}
throw error;
}
}
}

EvenStreamer Pattern

class EventSteamer {
constructor() {
this.listeners = {};
}

on = (event, listener) => {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
};

off = (event, listener) => {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter(
(fn) => fn !== listener
);
}
};

emit = (event, data) => {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(data));
}
};
}

const es = new EventSteamer();

function handleData(data) {
console.log("Receiver data", data);
}

es.on("data", handleData);
es.emit("data", "Sample Data"); //Receiver data Sample Data
es.off("data", handleData);
es.emit("data", "Another Data"); //No data as the handler has been removed

Iterator Pattern

const iterator = (collection) => {
let idx = 0;
return {
next: () =>
idx < collection.length
? { value: collection[idx++], done: false }
: { done: true },
};
};

const arr = [1, 2, 3, 4];
const iter = iterator(arr);
console.log(iter.next()); //{ value: 1, done: false }
console.log(iter.next()); //{ value: 2, done: false }
console.log(iter.next()); //{ value: 3, done: false }
console.log(iter.next()); //{ value: 4, done: false }
console.log(iter.next()); //{ done: true }

Observer Pattern

class Observer {
update = (message) => {
console.log("Received Message", message);
};
}

class Subject {
constructor() {
this.observers = [];
}

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

removerObserver = (observer) => {
this.observers = this.observers.filter((obs) => obs !== observer);
};

notifyObservers = (message) => {
this.observers.forEach((obs) => obs.update(message));
};
}

const subject = new Subject();

const ob1 = new Observer();
const ob2 = new Observer();
subject.addObserver(ob1);
subject.addObserver(ob2);

subject.notifyObservers("Hello Observers");
subject.removerObserver(ob1);
subject.notifyObservers("Observers, are you still there?");

PubSub Pattern

class PubSub {
constructor() {
this.subscribers = {};
}

subscribe = (event, cb) => {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(cb);
};

publish = (event, data) => {
if (this.subscribers[event]) {
this.subscribers[event].forEach((cb) => cb(data));
}
};

unSubscribe = (event, callback) => {
if (this.subscribers[event]) {
this.subscribers[event] = this.subscribers[event].filter(
(cb) => cb !== callback
);
}
};
}

const ps = new PubSub();

function subscriber1(data) {
console.log("Subscriber1 recieved data", data);
}

function subscriber2(data) {
console.log("Subscriber2 recieved data", data);
}

ps.subscribe("myEvent", subscriber1); //Subscriber1 recieved data Hello Love...
ps.subscribe("myEvent", subscriber2); //Subscriber2 recieved data Hello Love...
ps.publish("myEvent", "Hello Love...");

ps.unSubscribe("myEvent", subscriber1);

ps.publish("myEvent", "unsubscribed from s1"); //Subscriber2 recieved data unsubscribed from s1