Design Patterns yang Wajib Developer Ketahui (dengan Contoh JavaScript)

Design patterns bukan teori akademis — mereka solusi yang sudah terbukti untuk masalah yang berulang. Ini 5 pattern paling berguna dengan contoh kode nyata.
Design Patterns yang Wajib Developer Ketahui (dengan Contoh JavaScript)

Kenapa Belajar Design Patterns?

Design patterns memberikan kosakata yang sama antara developer. Ketika kamu bilang "ini pakai Observer pattern", developer lain langsung paham strukturnya tanpa perlu penjelasan panjang. Selain itu, patterns menawarkan solusi yang sudah teruji untuk masalah yang akan kamu hadapi berulang-ulang.

1. Singleton Pattern

Memastikan hanya ada satu instance dari sebuah class dan menyediakan titik akses global ke instance itu.

class DatabaseConnection {
  static #instance = null;
  #connection;

  constructor() {
    if (DatabaseConnection.#instance) {
      return DatabaseConnection.#instance;
    }
    this.#connection = this.#createConnection();
    DatabaseConnection.#instance = this;
  }

  #createConnection() {
    return { host: process.env.DB_HOST, status: "connected" };
  }

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

const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true

2. Observer Pattern

Mendefinisikan relasi one-to-many: saat satu objek berubah state, semua dependan-nya dinotifikasi otomatis. Dasar dari event system dan reactive programming.

class EventEmitter {
  #listeners = new Map();

  on(event, callback) {
    if (!this.#listeners.has(event)) this.#listeners.set(event, []);
    this.#listeners.get(event).push(callback);
    return () => this.off(event, callback);
  }

  off(event, callback) {
    const cbs = this.#listeners.get(event) || [];
    this.#listeners.set(event, cbs.filter(cb => cb !== callback));
  }

  emit(event, data) {
    (this.#listeners.get(event) || []).forEach(cb => cb(data));
  }
}

const store = new EventEmitter();
const unsubscribe = store.on("userLoggedIn", (user) => console.log(`Welcome, ${user.name}`));
store.emit("userLoggedIn", { name: "Khalim" }); // "Welcome, Khalim"

3. Factory Pattern

Mendelegasikan logika pembuatan objek ke factory, sehingga caller tidak perlu tahu class konkret yang dibuat.

function createNotification(type, message) {
  const types = {
    email: { channel: "email", priority: "low", icon: "?" },
    sms:   { channel: "sms",   priority: "high", icon: "?" },
    push:  { channel: "push",  priority: "medium", icon: "?" },
  };
  if (!types[type]) throw new Error(`Unknown notification type: ${type}`);
  return { ...types[type], message, createdAt: new Date() };
}

const notif = createNotification("sms", "Kode OTP kamu: 123456");

4. Strategy Pattern

Mendefinisikan family of algorithm, enkapsulasi masing-masing, dan membuatnya bisa ditukar. Menghilangkan if-else panjang untuk pemilihan algoritma.

const sortStrategies = {
  bubble: (arr) => { /* bubble sort */ },
  quick:  (arr) => [...arr].sort((a, b) => a - b),
  merge:  (arr) => { /* merge sort */ },
};

class Sorter {
  #strategy;
  constructor(strategy = "quick") {
    this.#strategy = sortStrategies[strategy];
  }
  setStrategy(strategy) { this.#strategy = sortStrategies[strategy]; }
  sort(arr) { return this.#strategy(arr); }
}

const sorter = new Sorter("quick");
sorter.sort([3, 1, 4, 1, 5]);

5. Decorator Pattern

Menambahkan behavior ke objek secara dinamis tanpa mengubah class aslinya. Inilah yang menjadi inspirasi decorator di TypeScript dan Python.

function withLogging(fn) {
  return function(...args) {
    console.log(`Calling ${fn.name} with`, args);
    const result = fn(...args);
    console.log(`Result:`, result);
    return result;
  };
}

function add(a, b) { return a + b; }
const loggedAdd = withLogging(add);
loggedAdd(2, 3); // Log input dan output secara otomatis

Butuh Solusi Digital Custom?

Kami siap membuatkan solusi digital sesuai kebutuhan bisnis Anda.

Konsultasi Gratis