Design Patterns

📖 Concept

Design patterns are proven solutions to common programming problems. They provide templates for writing maintainable, scalable, and reusable code.

Creational: Singleton, Factory Structural: Module, Proxy, Decorator Behavioral: Observer, Strategy, Pub/Sub, Command

🏠 Real-world analogy: Design patterns are like architectural blueprints. The "Observer" pattern is like a newspaper subscription — when news (data) changes, all subscribers (components) are automatically notified. No need to check manually.

💻 Code Example

codeTap to expand ⛶
1// Module Pattern (encapsulation)
2const UserModule = (() => {
3 let users = []; // Private
4 return {
5 add(user) { users.push(user); },
6 getAll() { return [...users]; },
7 findById(id) { return users.find(u => u.id === id); }
8 };
9})();
10
11// Singleton Pattern
12class Database {
13 constructor() {
14 if (Database.instance) return Database.instance;
15 this.connection = "connected";
16 Database.instance = this;
17 }
18 static getInstance() {
19 if (!Database.instance) new Database();
20 return Database.instance;
21 }
22}
23
24// Observer Pattern (Event Emitter)
25class EventEmitter {
26 constructor() { this.events = {}; }
27 on(event, listener) {
28 (this.events[event] ||= []).push(listener);
29 return () => this.off(event, listener); // Return unsubscribe
30 }
31 off(event, listener) {
32 this.events[event] = this.events[event]?.filter(l => l !== listener);
33 }
34 emit(event, ...args) {
35 this.events[event]?.forEach(listener => listener(...args));
36 }
37}
38
39// Factory Pattern
40class Shape {
41 static create(type, ...args) {
42 switch (type) {
43 case "circle": return new Circle(...args);
44 case "square": return new Square(...args);
45 case "triangle": return new Triangle(...args);
46 default: throw new Error(`Unknown shape: ${type}`);
47 }
48 }
49}
50
51// Strategy Pattern
52class Sorter {
53 constructor(strategy) { this.strategy = strategy; }
54 sort(data) { return this.strategy(data); }
55 setStrategy(strategy) { this.strategy = strategy; }
56}
57const bubbleSort = (data) => { /* ... */ return data; };
58const quickSort = (data) => { /* ... */ return data; };
59const sorter = new Sorter(quickSort);
60
61// Pub/Sub Pattern
62class PubSub {
63 constructor() { this.topics = {}; }
64 subscribe(topic, handler) {
65 (this.topics[topic] ||= []).push(handler);
66 return { unsubscribe: () => {
67 this.topics[topic] = this.topics[topic].filter(h => h !== handler);
68 }};
69 }
70 publish(topic, data) {
71 this.topics[topic]?.forEach(handler => handler(data));
72 }
73}

🏋️ Practice Exercise

Mini Exercise:

  1. Implement a Pub/Sub system for a todo app (add, complete, delete events)
  2. Create a Factory that produces different notification types (email, SMS, push)
  3. Implement the Strategy pattern for different payment methods
  4. Build an Observer pattern for a reactive state management system

⚠️ Common Mistakes

  • Overusing Singletons — they create hidden global state and make testing harder

  • Observer pattern without cleanup — forgetting to unsubscribe causes memory leaks

  • Factory pattern overkill — don't use a factory when a simple constructor suffices

  • Forcing patterns where they don't fit — patterns solve specific problems; don't use them just because

  • Not considering JavaScript-specific patterns — many OOP patterns from Java/C++ have simpler JS alternatives

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Design Patterns. Login to unlock this feature.