Michael Ouroumis logoichael Ouroumis

Design Patterns in JavaScript: Observer, Singleton, Factory & Strategy Explained

Design Patterns in JavaScript: Observer, Singleton, Factory & Strategy Explained

Why JavaScript Design Patterns Matter for Scalable Apps

JavaScript design patterns offer proven, reusable solutions to common coding problems. Whether you’re building a React front-end or a Node.js API, leveraging patterns like Observer, Singleton, Factory and Strategy helps you:

  • Create modular code that’s easy to maintain
  • Improve testability by reducing tight coupling
  • Scale projects without introducing fragile dependencies
  • Communicate architecture intent clearly across teams

If you’ve explored SOLID principles in React and JavaScript or studied Big O notation, adding these patterns to your toolkit will level up your codebase.


1. Observer Pattern in JavaScript

“One-to-many” subscription model: notify all observers when state changes.

In the Observer Pattern, a subject maintains a list of observers (subscribers). When the subject’s state changes, it notifies every observer—ideal for event systems and real-time updates.

Real-World Analogy

YouTube creators publish a video and all subscribers receive a notification.

JavaScript Example

class Observable { constructor() { this.subscribers = [] } subscribe(callback) { this.subscribers.push(callback) } notify(data) { this.subscribers.forEach((cb) => cb(data)) } } const newsFeed = new Observable() newsFeed.subscribe((news) => console.log('Subscriber A:', news)) newsFeed.subscribe((news) => console.log('Subscriber B:', news)) newsFeed.notify('New JavaScript design patterns article published!')

2. Singleton Pattern in JavaScript

Ensure only one instance exists and provide global access.

The Singleton Pattern creates a single, shared instance—perfect for global configuration, logging services, or database connections.

Real-World Analogy

An application’s configuration object (theme, locale) used anywhere without multiple instantiations.

JavaScript Example

const AppConfig = (function () { let instance function createInstance() { return { theme: 'dark', locale: 'en-US' } } return { getInstance() { if (!instance) instance = createInstance() return instance }, } })() const cfg1 = AppConfig.getInstance() const cfg2 = AppConfig.getInstance() console.log(cfg1 === cfg2) // true

3. Factory Pattern in JavaScript

Abstract object creation behind a function.

A Factory Function encapsulates how objects are created. You call the factory with parameters, and it returns fully formed instances without using new directly.

Real-World Analogy

A bakery’s cake factory produces different flavors based on order specs—customers don’t see the baking details.

JavaScript Example

function createUser(name, role) { return { name, role, describe() { console.log(`${this.name} is a ${this.role}`) }, } } const admin = createUser('Alice', 'Admin') const guest = createUser('Bob', 'Guest') admin.describe() // Alice is a Admin

4. Strategy Pattern in JavaScript

Define interchangeable algorithms behind a common interface.

The Strategy Pattern lets you swap behavior at runtime. You pass different strategy objects to a context, which delegates execution to the selected algorithm.

Real-World Analogy

Selecting payment methods in checkout: credit card, PayPal, or crypto—all follow a standard “pay” interface.

JavaScript Example

class ShippingContext { constructor(strategy) { this.strategy = strategy } setStrategy(strategy) { this.strategy = strategy } calculate(package) { return this.strategy.calculate(package) } } class FedEx { calculate(pkg) { return 10 } } class UPS { calculate(pkg) { return 15 } } const context = new ShippingContext(new FedEx()) console.log('FedEx cost:', context.calculate({ weight: 2 })) context.setStrategy(new UPS()) console.log('UPS cost:', context.calculate({ weight: 2 }))

Conclusion

Mastering these four core JavaScript design patterns—Observer, Singleton, Factory, and Strategy—gives you a robust foundation for building clean, scalable, and maintainable code. Integrate them alongside SOLID principles and algorithmic best practices to confidently tackle complex applications.

“The best code is not the one that works. It's the one that's easy to change.” — Unknown

Enjoyed this post? Share it: