Skip to content
Plurafy

Design Patterns in JavaScript

Common design patterns and their applications

Apply proven design patterns to write more maintainable, scalable, and reusable code.

Design patterns are reusable solutions to common programming problems. They provide templates for solving issues.

// Only one instance exists
class Database {
static instance = null;
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = null;
Database.instance = this;
}
connect() {
this.connection = 'connected';
}
}
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true (same instance)
// Create objects without specifying exact classes
class CarFactory {
static createCar(type) {
switch(type) {
case 'sedan':
return new Sedan();
case 'suv':
return new SUV();
default:
throw new Error('Unknown type');
}
}
}
class Sedan {
drive() { return 'Driving sedan'; }
}
class SUV {
drive() { return 'Driving SUV'; }
}
const car = CarFactory.createCar('sedan');
console.log(car.drive()); // 'Driving sedan'
// Notify multiple subscribers of events
class EventEmitter {
constructor() {
this.events = {};
}
on(event, handler) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(handler);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(handler => handler(...args));
}
}
}
const emitter = new EventEmitter();
emitter.on('user-login', (user) => {
console.log(`${user} logged in`);
});
emitter.emit('user-login', 'John');
// Different algorithms for same task
class PaymentProcessor {
constructor(strategy) {
this.strategy = strategy;
}
process(amount) {
return this.strategy.pay(amount);
}
}
class CreditCardStrategy {
pay(amount) {
return `Paid ${amount} via credit card`;
}
}
class PayPalStrategy {
pay(amount) {
return `Paid ${amount} via PayPal`;
}
}
const processor = new PaymentProcessor(new CreditCardStrategy());
console.log(processor.process(100)); // 'Paid 100 via credit card'
processor.strategy = new PayPalStrategy();
console.log(processor.process(100)); // 'Paid 100 via PayPal'
// Encapsulate private and public interfaces
const CounterModule = (() => {
let count = 0; // Private
return {
increment() { // Public
count++;
return count;
},
decrement() { // Public
count--;
return count;
}
};
})();
console.log(CounterModule.increment()); // 1
console.log(CounterModule.increment()); // 2
// count is not accessible directly
// Make incompatible interfaces work together
class OldAPI {
getOldData() {
return { name: 'John', age: 30 };
}
}
class APIAdapter {
constructor(oldAPI) {
this.oldAPI = oldAPI;
}
getNewData() {
const data = this.oldAPI.getOldData();
return {
fullName: data.name,
userAge: data.age
};
}
}
const oldAPI = new OldAPI();
const adapter = new APIAdapter(oldAPI);
console.log(adapter.getNewData());
// { fullName: 'John', userAge: 30 }
// Add behavior to objects dynamically
class Component {
render() {
return 'Component';
}
}
function withBorder(component) {
const originalRender = component.render.bind(component);
return {
render() {
return `[Border: ${originalRender()}]`;
}
};
}
function withShadow(component) {
const originalRender = component.render.bind(component);
return {
render() {
return `[Shadow: ${originalRender()}]`;
}
};
}
let comp = new Component();
comp = withBorder(comp);
comp = withShadow(comp);
console.log(comp.render()); // '[Shadow: [Border: Component]]'
// Chain operations/transformations
class Pipeline {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
return this;
}
async execute(data) {
let result = data;
for (const middleware of this.middlewares) {
result = await middleware(result);
}
return result;
}
}
const pipeline = new Pipeline();
pipeline
.use(async (data) => {
data.step1 = 'done';
return data;
})
.use(async (data) => {
data.step2 = 'done';
return data;
});
pipeline.execute({}).then(console.log);
// { step1: 'done', step2: 'done' }