DRY (Don't Repeat Yourself) artinya tiap pengetahuan di program punya satu tempat. Yang dihitung bukan teks yang mirip, tapi aturan yang sama. Bayangin harga kaos Raka yang ditulis di tiga tempat: katalog Instagram, halaman toko, dan nota. Harga naik, kamu ganti katalog dan halaman toko, notanya lupa. Pembeli lihat 80.000, nota bilang 75.000. Gak ada yang protes saat itu juga, tapi angkanya sudah gak cocok.
🧪 Tebakan cepat: cara menulis rupiah di struk ada di tiga fungsi. Raka minta formatnya diganti, dan kamu cuma mengubah dua dari tiga. Yang terjadi: error, atau struk yang campur aduk? Tebak dulu, jawabannya ada di bagian "Duplikasi gak bikin error".
Struk dengan tiga pemformat
Struk Toko Kaos Raka untuk keranjang A (2 kaos polos, 1 kaos sablon, voucher HEMAT10):
function itemLine(item) {
return item.name + ' x' + item.qty + ': Rp ' + (item.price * item.qty).toLocaleString('id-ID');
}
function savingLine(amount) {
return 'Hemat: Rp ' + amount.toLocaleString('id-ID');
}
function totalLine(amount) {
return 'Total: Rp ' + amount.toLocaleString('id-ID');
}
function printReceipt(cart, saving, total) {
for (const item of cart) console.log(itemLine(item));
console.log(savingLine(saving));
console.log(totalLine(total));
}
printReceipt(cart, 24500, 220500);
Kaos Polos Hitam x2: Rp 150.000
Kaos Sablon Kopi x1: Rp 95.000
Hemat: Rp 24.500
Total: Rp 220.500
Cara menulis rupiah, 'Rp ' + angka.toLocaleString('id-ID'), adalah satu pengetahuan. Di sini ia tertulis tiga kali.
Duplikasi gak bikin error
Raka mulai jualan ke luar negeri dan minta format IDR 220.500. Kamu mengubah itemLine dan totalLine, savingLine kelupaan:
Kaos Polos Hitam x2: IDR 150.000
Kaos Sablon Kopi x1: IDR 95.000
Hemat: Rp 24.500
Total: IDR 220.500
Gak ada error, programnya jalan terus. Itu jawaban tebakannya: duplikasi gak bikin program berhenti, cuma bikin hasilnya diam-diam gak konsisten. Makin banyak salinannya, makin besar peluang ada yang terlewat.
Satu fungsi, satu tempat
Pindahkan pengetahuannya ke satu fungsi, lalu semua tempat memanggil fungsi itu:
function formatRupiah(amount) {
return 'Rp ' + amount.toLocaleString('id-ID');
}
function itemLine(item) {
return item.name + ' x' + item.qty + ': ' + formatRupiah(item.price * item.qty);
}
function savingLine(amount) {
return 'Hemat: ' + formatRupiah(amount);
}
function totalLine(amount) {
return 'Total: ' + formatRupiah(amount);
}
- Output-nya sama persis dengan sebelumnya. Ini refactor.
- Ganti format sekarang cukup di satu baris,
formatRupiah, dan keempat baris struk ikut. toLocaleStringcuma muncul sekali di seluruh kode. Itu cara gampang mengecek duplikasinya sudah hilang.formatRupiahini sama dengan yang lahir di lesson Fungsi Kecil & Fokus.
Hal yang sama berlaku untuk angka. Kalau tarif pajak 0.11 ditulis di tiga baris (price * 0.11, shipping * 0.11, service * 0.11), ekstrak ke sebuah konstanta: const TAX_RATE = 0.11;, lalu ketiganya memakai TAX_RATE. Tarif berubah, satu baris yang diubah. Konstanta VOUCHER_DISCOUNT, FREE_SHIPPING_MIN dan SHIPPING_FEE di kasir Raka adalah contoh yang sama.
Mirip belum tentu sama
Raka juga punya reseller dengan potongan kontrak 10%. Kode kasirnya punya dua angka yang kebetulan sama:
const VOUCHER_DISCOUNT = 0.1; // promo HEMAT10, diatur marketing
const RESELLER_DISCOUNT = 0.1; // kontrak reseller, tertulis di perjanjian
Godaannya: "dua-duanya 0.1, gabung jadi DISCOUNT_RATE". Bulan depan marketing menaikkan promo jadi 15% dan mengubah DISCOUNT_RATE jadi 0.15. Reseller yang belanja Rp 245.000 ikut dapat 15%: bayar Rp 208.250, padahal kontraknya 10%, Rp 220.500.
Dua kode yang terlihat sama tapi berubah karena alasan berbeda disebut coincidental duplication (duplikasi kebetulan). Menggabungnya bikin abstraksi yang salah: satu perubahan merembet ke tempat yang gak seharusnya. Sandi Metz menulis (2016): "duplication is far cheaper than the wrong abstraction", duplikasi jauh lebih murah daripada abstraksi yang salah.
Pertanyaan pengujinya: kalau aturan yang satu berubah, apakah yang lain wajib ikut berubah? Format rupiah di tiga baris struk: ya, jadi gabung. Promo marketing dan kontrak reseller: tidak, jadi biarkan dua. Kalau ragu, banyak developer menunggu pola yang sama muncul tiga kali (rule of three) sebelum menggabung.
Latihan: formatRupiah
Di latihan, showProduct, showCartTotal dan showDiscount masing-masing memformat sendiri. Buat function formatRupiah(amount) yang mengembalikan 'Rp ' + amount.toLocaleString('id-ID'), lalu ketiga fungsi memanggilnya. toLocaleString cuma boleh tertulis sekali di seluruh kode. Output-nya tetap:
Laptop: Rp 15.000.000
Total: Rp 400.000
Rp 200.000 -> Rp 150.000
formatRupiah juga diuji sendiri dengan angka lain: formatRupiah(1500) harus 'Rp 1.500'.
⚠️ Jebakan yang sering ketemu
- Menggabung yang cuma kebetulan mirip. Promo dan kontrak reseller sama-sama 0.1 hari ini, tapi alasan berubahnya beda. Satu konstanta untuk keduanya adalah bug yang menunggu.
- Lupa satu salinan. Duplikasi gak bikin error, jadi salinan yang terlewat baru ketahuan saat ada yang membaca struknya.
- Sisa format di luar fungsi. Kalau
showCartTotalmasih memanggiltoLocaleStringsendiri, duplikasinya belum hilang dan aturan latihannya gagal. - Pemformat yang cuma benar untuk satu angka.
return 'Rp 200.000'lolos satu contoh, tapiformatRupiah(1500)harus'Rp 1.500'. - DRY bukan soal irit ketikan. Tujuannya satu sumber kebenaran. Nama yang lebih panjang tapi jelas tetap lebih baik.
TL;DR: DRY artinya tiap pengetahuan punya satu tempat. Cara menulis rupiah yang tersalin di tiga fungsi struk akan lupa diubah di salah satunya, dan hasilnya struk campur IDR dan Rp tanpa error. Pindahkan ke formatRupiah(amount), panggil dari semua tempat, dan pastikan toLocaleString cuma muncul sekali; angka yang berulang seperti 0.11 diekstrak ke konstanta (TAX_RATE). Tapi dua kode yang kebetulan mirip dan berubah karena alasan berbeda (promo vs kontrak reseller) jangan digabung: duplikasi lebih murah daripada abstraksi yang salah. Ujinya: kalau yang satu berubah, apakah yang lain wajib ikut?