DEV Community

Cover image for Understanding Core JavaScript Design Patterns
Biswas Prasana Swain
Biswas Prasana Swain

Posted on

Understanding Core JavaScript Design Patterns

If you’ve been coding in JavaScript for a while, you might have heard about design patterns. But what are they?

Think of design patterns like recipes for solving common problems in programming. Just like a cookie recipe tells you how to mix ingredients to get a perfect cookie, design patterns tell you how to structure your code so it works well, is easy to read, and doesn’t break easily.

Here, we’ll look at four super important patterns: Singleton, Module, Factory, and Observer. Let’s dive in!


1. Singleton Pattern

Idea: Only allow one instance of something to exist.

Analogy: Imagine the principal of a school. There’s only one principal, and everyone talks to that same person if they need help.

Why use it? When you want one object to control something globally, like a game score manager, or a database connection.

JavaScript Example:

const GameManager = (function () {
  let instance; // this will hold the single instance

  function init() {
    let score = 0;

    return {
      increaseScore: function () {
        score++;
        console.log(`Score: ${score}`);
      },
      resetScore: function () {
        score = 0;
        console.log(`Score reset`);
      }
    };
  }

  return {
    getInstance: function () {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  };
})();

// Usage
const game1 = GameManager.getInstance();
game1.increaseScore(); // Score: 1

const game2 = GameManager.getInstance();
game2.increaseScore(); // Score: 2 (same instance!)
Enter fullscreen mode Exit fullscreen mode

✅ Notice how game1 and game2 are the same instance.


2. Module Pattern

Idea: Keep code organized and hide some parts from the outside world.

Analogy: A backpack. You can keep some things inside (like a pencil case), but only let people see or use what you want them to.

Why use it? To encapsulate code, keeping it neat and preventing accidental changes from outside.

JavaScript Example:

const Calculator = (function () {
  // Private functions
  function add(a, b) {
    return a + b;
  }
  function subtract(a, b) {
    return a - b;
  }

  // Public API
  return {
    sum: add,
    difference: subtract
  };
})();

// Usage
console.log(Calculator.sum(5, 3));        // 8
console.log(Calculator.difference(5, 3)); // 2
Enter fullscreen mode Exit fullscreen mode

✅ Here, add and subtract are hidden inside the module, but we can still use them through Calculator.sum and Calculator.difference.


3. Factory Pattern

Idea: Create objects without specifying the exact class/type.

Analogy: A toy factory. You say "make me a toy," and it gives you a toy depending on your choice like car, doll, or robot, without you building it yourself.

Why use it? When you need lots of similar objects but don’t want to manually create each one.

JavaScript Example:

class Dog {
  speak() {
    console.log("Woof!");
  }
}

class Cat {
  speak() {
    console.log("Meow!");
  }
}

class AnimalFactory {
  static createAnimal(type) {
    if (type === "dog") return new Dog();
    if (type === "cat") return new Cat();
    throw new Error("Unknown animal type");
  }
}

// Usage
const myPet = AnimalFactory.createAnimal("dog");
myPet.speak(); // Woof!

const neighborPet = AnimalFactory.createAnimal("cat");
neighborPet.speak(); // Meow!
Enter fullscreen mode Exit fullscreen mode

✅ You don’t need to know the details of Dog or Cat. The factory does the work.


4. Observer Pattern

Idea: Let objects subscribe to events and react when those events happen.

Analogy: Think of YouTube notifications. You subscribe to a channel, and whenever the creator uploads a new video, you get notified automatically.

Why use it? When one object changes and many others need to know about it.

JavaScript Example:

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

  subscribe(observer) {
    this.observers.push(observer);
  }

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

  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  constructor(name) {
    this.name = name;
  }
  update(data) {
    console.log(`${this.name} received: ${data}`);
  }
}

// Usage
const news = new Subject();

const alice = new Observer("Alice");
const bob = new Observer("Bob");

news.subscribe(alice);
news.subscribe(bob);

news.notify("New JavaScript tutorial!");  
// Alice received: New JavaScript tutorial!
// Bob received: New JavaScript tutorial!
Enter fullscreen mode Exit fullscreen mode

✅ Any observer subscribed to news automatically gets updates.


Conclusion

Design patterns might sound fancy, but they’re really just smart ways to organize your code. Here’s a quick recap:

Pattern Idea Example Use Case
Singleton Only one instance Game manager, DB connection
Module Hide private stuff, expose public API Calculator, Utilities
Factory Create objects without knowing details Toy factory, Animal creator
Observer Notify objects of changes Chat apps, YouTube subs

Top comments (0)