🏠 Home / Hub

🟨 JS Lesson 13 — Design Patterns

← Back to JS Menu  |  🏠 Hub

Design Patterns ဆိုတာ ဘာလဲ?

Design Patterns = software design ပြဿနာတွေကို ဖြေရှင်းဖူးသော proven solutions
Creational · Structural · Behavioral — ၃ မျိုး ခွဲထားတယ်
PatternCategoryPurpose
SingletonCreationalInstance တစ်ခုပဲ ရှိစေ
FactoryCreationalObject ဆောက်နည်းကို encapsulate
ObserverBehavioralEvent/subscription system
StrategyBehavioralAlgorithm ကို swap လုပ်နိုင်
ModuleStructuralPrivate scope, public API

1. Singleton Pattern

// Singleton = class instance တစ်ခုပဲ ရှိနိုင် (e.g., Config, Logger, DB connection)

class Config {
  static #instance = null;
  #settings = {};

  constructor() {
    if (Config.#instance) {
      return Config.#instance;  // existing instance ပြန်ပေး
    }
    this.#settings = { theme: "dark", lang: "en", version: "1.0" };
    Config.#instance = this;
  }

  get(key) { return this.#settings[key]; }
  set(key, value) { this.#settings[key] = value; }
  getAll() { return { ...this.#settings }; }
}

const c1 = new Config();
const c2 = new Config();

c1 === c2;           // true — same instance!
c1.set("theme", "light");
c2.get("theme");     // "light" — same object!

// Modern Singleton with module (cleaner)
// config.js module exports a single instance
// ES modules are singletons by default (cached after first import)

2. Factory Pattern

// Factory = object creation ကို central function/class ထဲ ထားခြင်း

class Button {
  constructor(text, color) {
    this.text  = text;
    this.color = color;
    this.type  = "generic";
  }
  render() { return `[${this.type.toUpperCase()} Button: ${this.text}]`; }
}

class PrimaryButton extends Button {
  constructor(text) { super(text, "blue"); this.type = "primary"; }
}

class DangerButton extends Button {
  constructor(text) { super(text, "red"); this.type = "danger"; }
}

class SuccessButton extends Button {
  constructor(text) { super(text, "green"); this.type = "success"; }
}

// Factory — caller doesn't need to know which class
class ButtonFactory {
  static create(type, text) {
    switch (type) {
      case "primary": return new PrimaryButton(text);
      case "danger":  return new DangerButton(text);
      case "success": return new SuccessButton(text);
      default: throw new Error(`Unknown button type: ${type}`);
    }
  }
}

const btn1 = ButtonFactory.create("primary", "Submit");
const btn2 = ButtonFactory.create("danger",  "Delete");
const btn3 = ButtonFactory.create("success", "Save");

btn1.render();  // "[PRIMARY Button: Submit]"
btn2.render();  // "[DANGER Button: Delete]"

// Add new type → only need to add case in factory, not change callers

3. Observer Pattern (EventEmitter)

// Observer = "publish/subscribe" — event ပေးပို့ → subscribers ခံယူ
// Real use: DOM events, Vue reactivity, Node.js EventEmitter

class EventEmitter {
  #events = {};

  on(event, listener) {
    if (!this.#events[event]) this.#events[event] = [];
    this.#events[event].push(listener);
    return this;  // chaining support
  }

  off(event, listener) {
    if (!this.#events[event]) return;
    this.#events[event] = this.#events[event].filter(l => l !== listener);
  }

  emit(event, ...args) {
    (this.#events[event] || []).forEach(listener => listener(...args));
  }

  once(event, listener) {
    const wrapper = (...args) => {
      listener(...args);
      this.off(event, wrapper);
    };
    this.on(event, wrapper);
  }
}

// Usage
const shop = new EventEmitter();

// Subscribe
shop.on("order", (item, qty) => console.log(`Order: ${qty}x ${item}`));
shop.on("order", (item)      => console.log(`Stock check for ${item}`));

shop.once("open", () => console.log("Shop opened! (once only)"));

// Emit events
shop.emit("order", "Book", 3);
// "Order: 3x Book"
// "Stock check for Book"

shop.emit("open");  // "Shop opened! (once only)"
shop.emit("open");  // nothing — once already fired

4. Strategy Pattern

// Strategy = algorithm ကို runtime မှာ swap လုပ်နိုင်
// e.g., payment methods, sorting algorithms, validation rules

class PaymentProcessor {
  #strategy;

  constructor(strategy) {
    this.#strategy = strategy;
  }

  setStrategy(strategy) {
    this.#strategy = strategy;
  }

  pay(amount) {
    return this.#strategy.process(amount);
  }
}

// Strategies
const CreditCard = {
  process(amount) {
    return `💳 Charged $${amount} via Credit Card (2% fee: $${(amount*0.02).toFixed(2)})`;
  }
};

const PayPal = {
  process(amount) {
    return `🔵 Sent $${amount} via PayPal (1.5% fee: $${(amount*0.015).toFixed(2)})`;
  }
};

const Crypto = {
  process(amount) {
    return `₿ Transferred $${amount} via Bitcoin (network fee: $2.00)`;
  }
};

const KPay = {
  process(amount) {
    return `📱 Paid $${amount} via KPay (0% fee!)`;
  }
};

const processor = new PaymentProcessor(CreditCard);
processor.pay(100);  // "💳 Charged $100 via Credit Card..."

processor.setStrategy(PayPal);
processor.pay(100);  // "🔵 Sent $100 via PayPal..."

processor.setStrategy(KPay);
processor.pay(100);  // "📱 Paid $100 via KPay..."
// New payment method → add new strategy, don't change PaymentProcessor

5. Module Pattern

// Module = encapsulate private state, expose public API
// Modern: use ES modules (import/export)

// IIFE Module (pre-modules)
const UserStore = (function() {
  let users = [];      // private
  let nextId = 1;      // private

  function findById(id) {  // private helper
    return users.find(u => u.id === id);
  }

  return {
    // Public API
    addUser(name, email) {
      const user = { id: nextId++, name, email };
      users.push(user);
      return user;
    },
    getUser(id) { return findById(id) || null; },
    getAllUsers() { return [...users]; },  // copy, not ref
    deleteUser(id) {
      const idx = users.findIndex(u => u.id === id);
      if (idx === -1) return false;
      users.splice(idx, 1);
      return true;
    },
    get count() { return users.length; }
  };
})();

UserStore.addUser("Ko Ko",   "ko@example.com");
UserStore.addUser("Ma Ma",   "ma@example.com");
UserStore.getUser(1);        // { id: 1, name: "Ko Ko", ... }
UserStore.count;             // 2
// UserStore.users → undefined (private)
💡 ES Modules (import/export) ကို prefer ပါ — Modern JS standard

← JS 12  |  Next: JS 14 → Error Handling →

📌 Study Checklist