🔍 What are Design Patterns?
In programming, design patterns are like reusable solutions for common coding problems. Instead of reinventing the wheel every time, developers use these patterns to make their code more structured, clean, and easy to maintain.
In Node.js, design patterns are very useful because they help organize applications that deal with asynchronous operations, modules, and scalability.
📦 Module Pattern
The Module Pattern is one of the most common in Node.js. It allows us to split our code into smaller, reusable files called modules.
- A module is like a toolbox that contains useful functions.
- You can import a module wherever you need it.
- It keeps code organized and avoids repeating the same code in multiple places.
Example (Node.js):
// Module Pattern
// mathModule.js
module.exports.add = (a, b) => a + b;
module.exports.subtract = (a, b) => a - b;
// app.js
const math = require('./mathModule');
console.log(math.add(5, 3)); // Output: 8
🧑🤝🧑 Singleton Pattern
The Singleton Pattern ensures that there is only one instance of a particular class or object during the entire runtime of the application.
- Think of it like a global manager that everyone uses.
- Useful for shared resources like database connections or configuration settings.
- Saves memory and ensures consistency.
Example (Node.js):
// Singleton Pattern
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connection = 'Connected to database';
DatabaseConnection.instance = this;
}
}
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // Output: true
🏭 Factory Pattern
The Factory Pattern is used when we need to create multiple objects, but the exact type of object may vary depending on conditions.
- Instead of directly creating objects with new, we use a factory function that decides which object to create.
- Helps when you need flexible object creation.
Example (Node.js):
// Factory Pattern
class Car {
constructor() {
this.type = 'Car';
}
}
class Bike {
constructor() {
this.type = 'Bike';
}
}
function vehicleFactory(vehicleType) {
if (vehicleType === 'car') return new Car();
if (vehicleType === 'bike') return new Bike();
}
const v1 = vehicleFactory('car');
const v2 = vehicleFactory('bike');
console.log(v1.type); // Output: Car
console.log(v2.type); // Output: Bike
🔄 Observer Pattern
The Observer Pattern is used when one object (called the subject) needs to automatically notify other objects (observers) when something changes.
- It’s like a subscribe and notify system.
- Commonly used in event-driven programming.
Example (Node.js):
// Observer Pattern
const EventEmitter = require('events');
const emitter = new EventEmitter();
// Observer 1
emitter.on('message', (msg) => {
console.log('Observer 1 received:', msg);
});
// Observer 2
emitter.on('message', (msg) => {
console.log('Observer 2 received:', msg);
});
// Subject sends a notification
emitter.emit('message', 'Hello Observers!');
⚡ Middleware Pattern
Although technically part of frameworks like Express.js, the Middleware Pattern is widely used in Node.js apps.
- Middleware functions act like filters that process requests before they reach the final response.
- Useful for logging, authentication, data validation, etc.
Example (Node.js):
// Middleware Pattern
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('Middleware executed');
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000, () => console.log('Server running on port 3000'));
📝 Summary
Design patterns in Node.js help developers solve common coding problems in a smart and reusable way. The Module Pattern keeps code organized, the Singleton Pattern ensures only one instance is created, the Factory Pattern helps with flexible object creation, the Observer Pattern enables event-based communication, and the Middleware Pattern makes request handling cleaner. Using these patterns makes Node.js applications easier to build, manage, and scale.