Decorator Pattern — Design Patterns

Decorator Pattern menambahkan behavior baru ke object yang sudah ada tanpa mengubah kode aslinya. Seperti membungkus kado — kado tetap sama, tapi tampilannya be

Decorator Pattern menambahkan behavior baru ke object yang sudah ada tanpa mengubah kode aslinya. Seperti membungkus kado — kado tetap sama, tapi tampilannya bertambah.

Analogi: Kopi hitam dasar. Tambah susu → latte. Tambah whipped cream → mocha. Setiap tambahan "mendekorasi" kopi tanpa mengubah kopi dasarnya.

Masalah yang diselesaikan:

Function Decorator (paling umum di JS):

// Decorator: tambah logging ke fungsi apapun
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; }
function multiply(a, b) { return a * b; }

const loggedAdd = withLogging(add);
loggedAdd(3, 4);
// "Calling add with: [3, 4]"
// "Result: 7"

Decorator: Timing

function withTiming(fn) {
  return function(...args) {
    const start = Date.now();
    const result = fn(...args);
    console.log(`${fn.name} took ${Date.now() - start}ms`);
    return result;
  };
}

const timedSort = withTiming(function sortArray(arr) {
  return [...arr].sort((a, b) => a - b);
});

timedSort([5, 3, 8, 1, 9]);
// "sortArray took 0ms"

Decorator: Caching/Memoization

function withCache(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      console.log("Cache hit!");
      return cache.get(key);
    }
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const cachedFetch = withCache(function fetchUser(id) {
  console.log(`Fetching user ${id}...`);
  return { id, name: "User " + id };
});

cachedFetch(1); // "Fetching user 1..."
cachedFetch(1); // "Cache hit!"

Composing multiple decorators:

function withRetry(fn, maxRetries = 3) {
  return function(...args) {
    for (let i = 0; i < maxRetries; i++) {
      try { return fn(...args); }
      catch (e) {
        if (i === maxRetries - 1) throw e;
        console.log(`Retry ${i + 1}/${maxRetries}...`);
      }
    }
  };
}

// Compose: logging + timing + retry
const robustFetch = withLogging(
  withTiming(
    withRetry(fetchData, 3)
  )
);

Decorator di dunia nyata:

Kapan pakai Decorator Pattern:

Yang akan kamu pelajari