If you're diving deeper into software development and want to write cleaner, more scalable code — design patterns are your new best friends.
In this guide, I’ll walk you through the 23 most commonly used design patterns, grouped under Creational, Structural, and Behavioral patterns — all explained simply and with real-world examples in JavaScript.
Let’s go.........
📦 Creational Design Patterns
These deal with object creation mechanisms. The idea is to abstract the instantiation logic so that the system is not tightly coupled to the specific classes.
1. Singleton Pattern
Only one instance of a class is created and shared.
Use case: Logger, Config manager, DB connection.
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
Singleton.instance = this;
}
}
const a = new Singleton();
const b = new Singleton();
console.log(a === b); // true
2. Factory Pattern
Creates objects without exposing the instantiation logic.
Use case: UI component factory, Notification sender (Email/SMS).
class Car {
drive() {
console.log("Driving a car");
}
}
class Bike {
drive() {
console.log("Riding a bike");
}
}
function VehicleFactory(type) {
if (type === "car") return new Car();
if (type === "bike") return new Bike();
}
const vehicle = VehicleFactory("bike");
vehicle.drive(); // Riding a bike
3. Abstract Factory Pattern
Factory of factories.
Use case: Theme factory for UI (DarkThemeFactory, LightThemeFactory)
function darkButton() {
return { color: "black" };
}
function lightButton() {
return { color: "white" };
}
function UIFactory(mode) {
return {
createButton: mode === "dark" ? darkButton : lightButton,
};
}
const factory = UIFactory("dark");
console.log(factory.createButton()); // { color: "black" }
4. Builder Pattern
Step-by-step creation of a complex object.
Use case: Building complex HTML, query builder, or form builders.
class BurgerBuilder {
constructor() {
this.burger = {};
}
addCheese() {
this.burger.cheese = true;
return this;
}
addLettuce() {
this.burger.lettuce = true;
return this;
}
build() {
return this.burger;
}
}
const burger = new BurgerBuilder().addCheese().addLettuce().build();
console.log(burger); // { cheese: true, lettuce: true }
5. Prototype Pattern
Clone an existing object instead of creating a new one.
Use case: Performance optimization, default object templates.
const car = {
wheels: 4,
drive() {
console.log("Driving...");
},
};
const myCar = Object.create(car);
console.log(myCar.wheels); // 4
🧱 Structural Design Patterns
These are about class and object composition, focusing on how large structures are built from smaller pieces.
6. Adapter Pattern
Converts one interface to another.
Use case: Legacy APIs, payment gateway adapters.
class OldPrinter {
oldPrint(msg) {
console.log("Old Print:", msg);
}
}
class Adapter {
constructor() {
this.oldPrinter = new OldPrinter();
}
print(msg) {
this.oldPrinter.oldPrint(msg);
}
}
const printer = new Adapter();
printer.print("Hello World");
7. Decorator Pattern
Add new functionality without modifying the original class.
Use case: Middleware, logging wrappers.
function coffee() {
return "coffee";
}
function withMilk(coffeeFunc) {
return () => coffeeFunc() + " + milk";
}
const fancyCoffee = withMilk(coffee);
console.log(fancyCoffee()); // coffee + milk
8. Facade Pattern
Simplifies a complex subsystem by exposing a high-level API.
Use case: Simplify media player libraries, SDKs.
class Engine {
start() {
console.log("Engine started");
}
}
class Lights {
on() {
console.log("Lights on");
}
}
class CarFacade {
constructor() {
this.engine = new Engine();
this.lights = new Lights();
}
startCar() {
this.engine.start();
this.lights.on();
}
}
const car = new CarFacade();
car.startCar();
9. Composite Pattern
Treat a group of objects the same way as a single object.
Use case: File system, nested UI elements.
class Leaf {
constructor(name) {
this.name = name;
}
show() {
console.log(this.name);
}
}
class Folder {
constructor(name) {
this.name = name;
this.children = [];
}
add(child) {
this.children.push(child);
}
show() {
console.log(this.name);
this.children.forEach(child => child.show());
}
}
const file1 = new Leaf("file1.txt");
const file2 = new Leaf("file2.txt");
const folder = new Folder("MyFolder");
folder.add(file1);
folder.add(file2);
folder.show();
10. Bridge Pattern
Separate abstraction from implementation.
Use case: Theme-switching, rendering strategies.
class LightTheme {
getColor() {
return "Light color";
}
}
class DarkTheme {
getColor() {
return "Dark color";
}
}
class Page {
constructor(theme) {
this.theme = theme;
}
getContent() {
return `Page with ${this.theme.getColor()}`;
}
}
const darkPage = new Page(new DarkTheme());
console.log(darkPage.getContent()); // Page with Dark color
11. Proxy Pattern
A placeholder that controls access to another object.
Use case: Lazy loading, access control, logging.
class RealImage {
display() {
console.log("Displaying image");
}
}
class ProxyImage {
constructor() {
this.realImage = null;
}
display() {
if (!this.realImage) {
this.realImage = new RealImage();
}
this.realImage.display();
}
}
const img = new ProxyImage();
img.display();
🧠 Behavioral Design Patterns
These are all about how objects interact with each other.
12. Observer Pattern
Notify all subscribers when something changes.
Use case: Event listeners, Pub/Sub.
class Subject {
constructor() {
this.subscribers = [];
}
subscribe(fn) {
this.subscribers.push(fn);
}
notify(data) {
this.subscribers.forEach(fn => fn(data));
}
}
const subject = new Subject();
subject.subscribe(console.log);
subject.notify("Hello!");
13. Strategy Pattern
Change the algorithm at runtime.
Use case: Sorting strategies, payment gateways.
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function calculate(strategy, a, b) {
return strategy(a, b);
}
console.log(calculate(add, 5, 3)); // 8
console.log(calculate(multiply, 5, 3)); // 15
14. Command Pattern
Encapsulate requests as objects.
Use case: Undo/Redo, menu actions.
class Light {
turnOn() {
console.log("Light on");
}
}
class Command {
constructor(device) {
this.device = device;
}
execute() {
this.device.turnOn();
}
}
const light = new Light();
const cmd = new Command(light);
cmd.execute();
15. Chain of Responsibility Pattern
Pass requests down a chain until someone handles it.
Use case: Express middlewares, support ticket routing.
function handler1(req, next) {
if (req.type === "log") console.log("Handler1 processed");
else next();
}
function handler2(req) {
console.log("Handler2 processed");
}
function processRequest(req) {
handler1(req, () => handler2(req));
}
processRequest({ type: "data" }); // Handler2 processed
16. Template Method Pattern
Define the skeleton of an algorithm, but let subclasses fill in the steps.
Use case: Framework hooks.
class Game {
start() {
this.init();
this.play();
this.end();
}
init() {}
play() {}
end() {}
}
class Football extends Game {
init() {
console.log("Football Initialized");
}
play() {
console.log("Playing Football");
}
end() {
console.log("Football Ended");
}
}
new Football().start();
17. State Pattern
Change behavior based on internal state.
Use case: Media players, form steps, UI modes.
class TrafficLight {
constructor() {
this.states = ["green", "yellow", "red"];
this.current = 0;
}
change() {
console.log("Light:", this.states[this.current]);
this.current = (this.current + 1) % this.states.length;
}
}
const light = new TrafficLight();
light.change(); // green
light.change(); // yellow
18. Mediator Pattern
Centralized communication between objects.
Use case: Chatrooms, component interactions.
class ChatRoom {
showMessage(user, message) {
console.log(`${user}: ${message}`);
}
}
const room = new ChatRoom();
room.showMessage("Alice", "Hello Bob!");
19. Visitor Pattern
Add operations to objects without changing their classes.
Use case: Compilers, object structure traversal.
class Book {
accept(visitor) {
visitor.visitBook(this);
}
}
class BookVisitor {
visitBook(book) {
console.log("Visiting book");
}
}
const book = new Book();
book.accept(new BookVisitor());
20. Interpreter Pattern
Build a simple language interpreter.
Use case: Regex, Expression evaluators.
class Number {
constructor(value) {
this.value = value;
}
interpret() {
return this.value;
}
}
const one = new Number(1);
const two = new Number(2);
console.log(one.interpret() + two.interpret()); // 3
21. Memento Pattern
Capture and restore an object's state.
Use case: Undo features, save/restore.
class Editor {
constructor() {
this.content = "";
}
write(text) {
this.content += text;
}
save() {
return this.content;
}
restore(saved) {
this.content = saved;
}
}
22. Iterator Pattern
Access elements without exposing the structure.
Use case: Custom collections.
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
23. Null Object Pattern
Use an object that does nothing instead of null.
Use case: Avoid if (obj) checks.
class NullUser {
getName() {
return "Guest";
}
}
function getUser(user) {
return user || new NullUser();
}
console.log(getUser(null).getName()); // Guest
Happy Coding 👨💻🔥


Top comments (0)