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:
- Ingin menambah fitur ke object/fungsi tanpa mengubah source code-nya
- Inheritance terlalu kaku — tidak bisa mix-and-match fitur
- Perlu compose behavior secara dinamis
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:
- Express middleware — setiap middleware "mendekorasi" request/response
- Higher-Order Components (HOC) di React —
withRouter(),connect() - Python decorators —
@app.route("/"),@login_required - TypeScript/TC39 decorators —
@Component,@Injectable
Kapan pakai Decorator Pattern:
- Saat ingin menambah logging, caching, retry, validation ke fungsi yang sudah ada
- Saat fitur bersifat cross-cutting (berlaku di banyak tempat)
- Saat ingin compose behavior secara fleksibel