Labs ICT
Pro Login

Common Patterns

Module Pattern

The module pattern uses closures to create private state and expose a public API. It's one of the most widely used JavaScript patterns for organizing code.

const Counter = (() => {
  let count = 0; // Private

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount() { return count; },
  };
})();

Counter.increment();
Counter.increment();
console.log(Counter.getCount()); // 2
console.log(count); // ❌ Not accessible

Observer Pattern

The observer pattern lets objects subscribe to and receive notifications from other objects. It's the basis of event systems and reactive programming.

class EventEmitter {
  constructor() {
    this.listeners = {};
  }

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(callback);
  }

  emit(event, ...args) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(cb => cb(...args));
    }
  }
}

const emitter = new EventEmitter();
emitter.on("data", (msg) => console.log("Received:", msg));
emitter.emit("data", "Hello!");

Singleton Pattern

The singleton ensures a class has only one instance and provides a global point of access to it. It's useful for shared resources like database connections or configuration.

class Database {
  static #instance = null;

  static getInstance() {
    if (!Database.#instance) {
      Database.#instance = new Database();
    }
    return Database.#instance;
  }

  connect() {
    console.log("Connected to database");
  }
}

const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true — same instance
Try it Yourself →

Factory Pattern

Factories create objects without using the new keyword. They're flexible — you can return different types of objects based on input.

function createUser(type) {
  const base = {
    name: "",
    login() { console.log(`${this.name} logged in`); },
  };

  switch (type) {
    case "admin":
      return { ...base, role: "admin", delete() {} };
    case "guest":
      return { ...base, role: "guest", browse() {} };
    default:
      return { ...base, role: "user" };
  }
}

const admin = createUser("admin");
admin.login(); // " logged in"

Summary

Design patterns are reusable solutions to common problems. The module, observer, singleton, and factory patterns are essential tools in every JavaScript developer's toolkit.