Refactoring patterns adalah katalog teknik yang sudah terbukti untuk memperbaiki struktur kode tanpa mengubah behavior. Martin Fowler mendokumentasikan lebih dari 70 pattern di bukunya "Refactoring".
Pattern 1: Extract Function Gunakan saat ada blok kode yang perlu komentar, atau kode yang sama diulang.
// SEBELUM
function printOwing(invoice) {
let outstanding = 0;
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
for (const o of invoice.orders) {
outstanding += o.amount;
}
const today = new Date();
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30);
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}
// SESUDAH
function printOwing(invoice) {
printBanner();
const outstanding = calculateOutstanding(invoice);
recordDueDate(invoice);
printDetails(invoice, outstanding);
}
function printBanner() {
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
}
function calculateOutstanding(invoice) {
return invoice.orders.reduce((sum, o) => sum + o.amount, 0);
}
function recordDueDate(invoice) {
const today = new Date();
invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30);
}
function printDetails(invoice, outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}
Pattern 2: Replace Conditional with Polymorphism Gunakan saat ada switch/if-else berdasarkan tipe objek.
// SEBELUM
function getSpeed(bird) {
switch (bird.type) {
case "EuropeanSwallow": return 35;
case "AfricanSwallow": return bird.numberOfCoconuts > 2 ? 0 : 40;
case "NorwegianParrot": return bird.isNailed ? 0 : 18 + bird.voltage / 10;
default: throw new Error(`Unknown bird: ${bird.type}`);
}
}
// SESUDAH
class EuropeanSwallow {
get speed() { return 35; }
}
class AfricanSwallow {
constructor(numberOfCoconuts) { this.numberOfCoconuts = numberOfCoconuts; }
get speed() { return this.numberOfCoconuts > 2 ? 0 : 40; }
}
class NorwegianParrot {
constructor(voltage, isNailed) { this.voltage = voltage; this.isNailed = isNailed; }
get speed() { return this.isNailed ? 0 : 18 + this.voltage / 10; }
}
Pattern 3: Introduce Parameter Object Gunakan saat banyak parameter yang selalu bersama.
// SEBELUM: 5 parameter
function createReport(startDate, endDate, minAmount, maxAmount, userId) { ... }
createReport("2024-01-01", "2024-12-31", 0, 1000000, 42);
// SESUDAH: 1 parameter object
function createReport(criteria) { ... }
createReport({
dateRange: { start: "2024-01-01", end: "2024-12-31" },
amountRange: { min: 0, max: 1000000 },
userId: 42,
});
Pattern 4: Move Function Gunakan saat fungsi lebih sering menggunakan data dari class/module lain.
// SEBELUM: accountType ada di Account, tapi fungsi ada di Customer
class Customer {
getDiscount() {
return this.account.accountType === "premium" ? 0.2 : 0.1;
}
}
// SESUDAH: pindahkan ke Account
class Account {
getDiscount() {
return this.accountType === "premium" ? 0.2 : 0.1;
}
}
class Customer {
getDiscount() { return this.account.getDiscount(); }
}
Pattern 5: Replace Temp with Query Gunakan saat variable temporary menampung hasil ekspresi yang bisa dijadikan fungsi.
// SEBELUM
function calculateTotal(order) {
const basePrice = order.quantity * order.itemPrice;
const discount = basePrice > 1000 ? 0.05 : 0;
return basePrice * (1 - discount);
}
// SESUDAH
function basePrice(order) { return order.quantity * order.itemPrice; }
function discountFactor(order) { return basePrice(order) > 1000 ? 0.05 : 0; }
function calculateTotal(order) { return basePrice(order) * (1 - discountFactor(order)); }
Workflow refactoring yang aman:
- Tulis test sebelum refactor (atau pastikan test sudah ada)
- Satu perubahan kecil per langkah
- Run test setelah setiap langkah
- Commit setelah setiap refactoring yang sukses
- Jangan refactor dan tambah fitur dalam commit yang sama
🎭 Analogi sehari-hari
Refactoring patterns itu kayak resep katalog koki profesional. Cara goreng telur ada nama: omelet, scrambled, sunny side up, poached. Tiap teknik dipake di kondisi tertentu, dengan langkah yang udah teruji puluhan tahun. Kamu gak perlu nemuin sendiri "cara goreng telur yang benar" — ikutin resep yang udah ada, hasil predictable, gak gosong. Refactoring patterns sama: 70+ teknik dari Martin Fowler dengan nama spesifik (Extract Function, Replace Conditional with Polymorphism, Introduce Parameter Object). Pas ngeliat kode jelek, kamu PUNYA vocabulary buat ngomong "ini perlu Extract Function" alih-alih "ini perlu dirapihin entah gimana caranya".
⚠️ Jebakan yang sering ditemui
- Refactor tanpa pattern eksplisit — "rapihin entah gimana" = inkonsisten + susah review. Pilih pattern yang spesifik.
- Apply pattern tanpa konteks — Replace Conditional with Polymorphism untuk if/else 2 case = overkill.
- Extract function tanpa nama bagus —
helper1(),processStuff()= belum solve masalah. - Big bang refactor combine multiple patterns — susah review + rollback. Apply ONE pattern per commit.
- Skip test dulu sebelum refactor — pasti break behavior tanpa kamu sadar.
- Refactor + add feature di PR sama — review-er bingung mana yang refactor, mana yang behavior baru.
- Nama pattern yang gak konsisten antar tim — tiap tim "Extract Method" vs "Extract Function" — pilih satu vocabulary.
Pattern Paling Sering Dipake
- Extract Function — pecah blok kode ke fungsi baru
- Inline Function — hapus fungsi yang gak nambah value
- Extract Variable — ekspresi kompleks → variable bernama
- Inline Variable — hapus variable yang trivial (
const x = 5; return x→return 5) - Rename Variable/Function — kasih nama lebih jelas
- Replace Magic Number with Constant —
429→HTTP_TOO_MANY_REQUESTS - Decompose Conditional — kondisi kompleks → fungsi terpisah
- Replace Loop with Pipeline —
for + push→map/filter/reduce - Replace Conditional with Polymorphism — banyak
switch (type)→ polymorphism - Introduce Parameter Object — fungsi 5+ param → bungkus jadi object
- Move Function — pindahin fungsi ke class/module yang lebih cocok
- Replace Temp with Query — variable temp → fungsi yang return value
Workflow Refactor Aman (Recap)
- Tulis test — safety net, must-have sebelum refactor
- Satu pattern per langkah — Extract Function, commit. Lalu Rename, commit. Bukan combine.
- Run test setelah tiap langkah — ngga lewatin step ini
- Commit per langkah — git history clean, rollback gampang kalo break
- Pisah refactor commit dari feature commit — review-able
🎯 Pilih pattern apa untuk masalah apa?
- Blok kode panjang dengan komentar "// step 1, 2, 3" → Extract Function (per step)
- Variable trivial yang gak nambah value → Inline Variable
- Ekspresi kompleks dipake berulang → Extract Variable + nama jelas
- Magic number/string → Replace with Named Constant
- Conditional 5+ branches berdasar type → Replace with Polymorphism
- Loop manual yang transform array → Replace with Pipeline (
map/filter/reduce)- Fungsi dengan 4+ parameter → Introduce Parameter Object
- Method di class yang gak fit → Move Function/Method
- Class God Object → Extract Class (pecah jadi multiple class)
Aturan: pilih pattern yang nge-target masalah SPESIFIK kamu. Jangan apply pattern karena "kerennya".
TL;DR: Refactoring patterns = katalog teknik proven (Martin Fowler 70+ patterns) buat improve kode tanpa ubah behavior. Vocabulary spesifik (Extract Function, Replace Conditional with Polymorphism) > "rapihin entah gimana". Top 5 paling sering: Extract Function, Rename, Extract Variable, Replace Magic Number, Decompose Conditional. Apply ONE pattern per commit, run test after each, pisah dari feature commit. Test = safety net wajib.