Fungsi yang baik melakukan satu hal. Ukurannya bukan jumlah baris, tapi apakah isinya bisa diceritakan dalam satu kalimat tanpa kata "dan". Bayangin resep nasi goreng. Di daftar menu cuma satu baris, tapi di dapur dipecah jadi langkah bernama: tumis bumbu, masukkan nasi, bumbui, sajikan. Kalau rasanya keasinan, kamu cukup memeriksa langkah "bumbui", gak perlu membaca resep dari awal.
🧪 Tebakan cepat: fungsi
calculateTotaldi bawah cuma 9 baris. Berapa hal yang dikerjakannya: satu, dua, atau empat? Tebak dulu, jawabannya ada di bagian "Satu kalimat tanpa dan".
Satu kalimat tanpa "dan"
Ini kasir Toko Kaos Raka setelah namanya dirapikan di lesson sebelumnya:
const VOUCHER_DISCOUNT = 0.1;
const FREE_SHIPPING_MIN = 200000;
const SHIPPING_FEE = 15000;
function calculateTotal(cart, voucherCode) {
let total = 0;
for (const item of cart) {
total += item.price * item.qty;
}
if (voucherCode === 'HEMAT10') total -= total * VOUCHER_DISCOUNT;
if (total < FREE_SHIPPING_MIN) total += SHIPPING_FEE;
return 'Total: Rp ' + total.toLocaleString('id-ID');
}
Ceritakan isinya dalam satu kalimat: calculateTotal menjumlahkan harga barang, dan memotong voucher, dan menambah ongkir, dan memformat jadi teks rupiah. Tiga "dan", jadi empat hal. Itu jawaban tebakannya: cuma 9 baris, tapi empat pekerjaan. Panjang fungsi bukan ukurannya, yang diukur adalah berapa hal yang dikerjakannya sendiri.
Pecah per langkah
Tiap langkah dipindah ke fungsi sendiri dengan nama kata kerja:
function calculateSubtotal(cart) {
let subtotal = 0;
for (const item of cart) {
subtotal += item.price * item.qty;
}
return subtotal;
}
function applyVoucher(amount, voucherCode) {
if (voucherCode === 'HEMAT10') return amount - amount * VOUCHER_DISCOUNT;
return amount;
}
function addShipping(amount) {
if (amount < FREE_SHIPPING_MIN) return amount + SHIPPING_FEE;
return amount;
}
function formatRupiah(amount) {
return 'Rp ' + amount.toLocaleString('id-ID');
}
function calculateTotal(cart, voucherCode) {
let total = calculateSubtotal(cart);
total = applyVoucher(total, voucherCode);
total = addShipping(total);
return 'Total: ' + formatRupiah(total);
}
- Tiap fungsi kecil bisa diceritakan tanpa "dan":
applyVouchermemotong voucher,addShippingmenambah ongkir. calculateTotalsekarang cuma merangkai. Empat barisnya dibaca seperti daftar isi.- Tiap langkah bisa dites sendiri:
applyVoucher(210000, 'HEMAT10')hasilnya189000,addShipping(189000)hasilnya204000. - Kode totalnya malah lebih panjang. Gak apa-apa: yang dihemat adalah waktu mencari, bukan baris.
Pecah tanpa mengubah urutan
Memecah fungsi adalah refactor, jadi hasilnya harus tetap. Jalankan empat keranjang dari lesson pertama sebelum dan sesudah memecah:
| Keranjang | Voucher | Hasil yang harus tetap |
|---|---|---|
| A: 2 kaos polos + 1 kaos sablon (245.000) | HEMAT10 |
Total: Rp 220.500 |
| A | tanpa | Total: Rp 245.000 |
| C: 1 kaos polos (75.000) | tanpa | Total: Rp 90.000 |
| D: 1 kaos polos + 1 kaos sablon + 1 topi (210.000) | HEMAT10 |
Total: Rp 204.000 |
Kalau waktu memecah dua baris di calculateTotal tertukar, ongkir dicek sebelum potongan:
total = addShipping(total); // tertukar
total = applyVoucher(total, voucherCode);
OK Total: Rp 220.500
OK Total: Rp 245.000
OK Total: Rp 90.000
BEDA Total: Rp 189.000, harusnya Total: Rp 204.000
Tiga keranjang tetap lolos, cuma D yang menangkapnya. Kabar baiknya, setelah dipecah urutan langkah terbaca jelas sebagai dua baris yang bersebelahan.
Fungsi perangkai juga melakukan satu hal
calculateTotal yang cuma memanggil empat langkah tetap melakukan satu hal: menghitung total. Keempat langkahnya bagian dari tugas yang sama. Lain cerita dengan fungsi seperti ini:
function processOrder(order) {
validateOrder(order);
calculateTotal(order);
sendConfirmationEmail(order);
updateInventory(order);
generateInvoice(order);
}
Walau sudah mendelegasikan, processOrder merangkai lima urusan dari bagian yang berbeda: pesanan, harga, email, stok gudang dan dokumen. Tiap kali salah satunya berubah syarat atau urutannya (misalnya email baru dikirim setelah pembayaran lunas), fungsi ini ikut diubah. Jadi ia masih melakukan terlalu banyak hal, dan lebih fokus kalau dipecah per urusan, misalnya langkah pesanan sendiri, lalu email, stok dan invoice ditangani setelah pesanan tersimpan.
Latihan: laporan nilai siswa
processStudents di latihan juga melakukan tiga hal sekaligus. Pecah jadi:
filterPassingStudents(students): mengembalikan siswa dengangrade >= 70.calculateAverage(students): mengembalikan rata-ratagrade. Daftar kosong mengembalikan0.formatReport(passing, average): mengembalikan teksLulus: X siswa, Rata-rata: Y, dengan X jumlah siswa dipassing.
processStudents tinggal memanggil ketiganya berurutan. Dari lima siswa, yang lulus Budi 85, Citra 92 dan Eka 78. Rata-ratanya (85 + 92 + 78) / 3 = 85, jadi output-nya tetap Lulus: 3 siswa, Rata-rata: 85.
Kenapa daftar kosong harus dicek? 0 / 0 di JavaScript hasilnya NaN (Not a Number), bukan 0. Tulis if (students.length === 0) return 0; sebelum membagi.
⚠️ Jebakan yang sering ketemu
- Mengubah urutan waktu memecah. Langkahnya benar, urutannya salah, dan cuma keranjang yang pas di batas aturan yang menangkapnya. Jalankan test sebelum dan sesudah.
- Memecah terlalu kecil.
function add(a, b) { return a + b; }yang dipanggil sekali gak menambah kejelasan. Pecah kalau namanya lebih jelas daripada isinya. - Nama pecahan yang kabur.
step1(),helper(),doStuff()gak menolong. Kalau susah memberi nama tanpa "dan", pecahnya belum pas. - Rata-rata dari daftar kosong.
total / students.lengthdengan daftar kosong hasilnyaNaN. Kembalikan 0 untuk daftar kosong. - Efek samping tersembunyi.
applyVoucheryang diam-diam juga mencatat voucher terpakai ke database bikin kaget. Nama harus jujur tentang semua yang dikerjakan.
TL;DR: Ukur fungsi dari berapa hal yang dikerjakannya, bukan dari panjangnya: ceritakan dalam satu kalimat, dan tiap "dan" berarti satu hal lagi. calculateTotal 9 baris ternyata mengerjakan empat hal, jadi dipecah jadi calculateSubtotal, applyVoucher, addShipping dan formatRupiah, dan calculateTotal tinggal merangkai. Tiap fungsi kecil bisa dites sendiri. Memecah adalah refactor: urutannya harus tetap, dan keempat keranjang (keranjang D Total: Rp 204.000 menangkap urutan yang tertukar) harus memberi hasil yang sama. Fungsi perangkai boleh, selama langkahnya satu tugas; processOrder yang merangkai lima urusan berbeda masih melakukan terlalu banyak. Di latihan: lulus >= 70, rata-rata daftar kosong 0 (bukan NaN), laporan Lulus: 3 siswa, Rata-rata: 85.