Refactoring adalah perubahan struktur kode tanpa mengubah perilaku eksternal. Tujuannya adalah meningkatkan readability, menurunkan kompleksitas, atau mempersiapkan kode untuk fitur baru. IntelliJ menyediakan berbagai operasi refactoring yang aman karena dilakukan dengan analisis semantik, bukan sekadar find-and-replace. Pada chapter ini kita akan membandingkan kode sebelum dan sesudah refactoring menggunakan beberapa operasi utama: rename, extract method, extract class, dan inline.
Operasi Refactoring di IntelliJ #
| Operasi | Shortcut | Tujuan |
|---|---|---|
| Rename | SHIFT+F6 | Ganti nama variabel/method/class secara aman |
| Extract Method | CTRL+ALT+M | Pindahkan blok kode ke method baru |
| Extract Class | (menu) | Pindahkan tanggung jawab ke class baru |
| Extract Variable | CTRL+ALT+V | Ekstrak ekspresi ke variabel lokal |
| Inline | CTRL+ALT+N | Balik dari method/variabel ke inline |
| Change Signature | CTRL+F6 | Ubah parameter method |
Demo Sebelum dan Sesudah Refactoring #
Program berikut memiliki fungsi panjang yang melakukan tiga hal: validasi, perhitungan diskon, dan formatting. Kita akan merefactor menjadi tiga class terpisah.
// Demo refactoring: rename, extract method, extract class, inline.
// Menunjukkan transformasi sebelum dan sesudah refactoring.
// === SEBELUM refactoring: fungsi panjang melakukan banyak hal ===
data class Pesanan(val id: Int, val item: List<String>, val pelanggan: String, val total: Double)
fun prosesSebelum(p: Pesanan): String {
// Validasi
if (p.pelanggan.isBlank()) {
return "Error: pelanggan kosong"
}
if (p.item.isEmpty()) {
return "Error: tidak ada item"
}
if (p.total < 0) {
return "Error: total negatif"
}
// Hitung diskon
var diskon = 0.0
if (p.total > 100_000) {
diskon = p.total * 0.10
} else if (p.total > 50_000) {
diskon = p.total * 0.05
}
val final = p.total - diskon
// Format pesan
val ringkas = "Pesanan #${p.id} oleh ${p.pelanggan}: ${p.item.size} item, " +
"total Rp${p.total}, diskon Rp$diskon, bayar Rp$final"
return ringkas
}
// === SETELAH refactoring: ekstrak fungsi dan class ===
class ValidasiPesanan {
fun periksa(p: Pesanan): String? = when {
p.pelanggan.isBlank() -> "pelanggan kosong"
p.item.isEmpty() -> "tidak ada item"
p.total < 0 -> "total negatif"
else -> null
}
}
class KalkulatorDiskon {
fun hitung(total: Double): Double = when {
total > 100_000 -> total * 0.10
total > 50_000 -> total * 0.05
else -> 0.0
}
}
class PeringkasPesanan {
fun ringkas(p: Pesanan, diskon: Double): String {
val final = p.total - diskon
return "Pesanan #${p.id} oleh ${p.pelanggan}: ${p.item.size} item, " +
"total Rp${p.total}, diskon Rp$diskon, bayar Rp$final"
}
}
class PemrosesPesanan(
private val validasi: ValidasiPesanan = ValidasiPesanan(),
private val diskon: KalkulatorDiskon = KalkulatorDiskon(),
private val peringkas: PeringkasPesanan = PeringkasPesanan()
) {
fun proses(p: Pesanan): String {
validasi.periksa(p)?.let { return "Error: $it" }
val d = diskon.hitung(p.total)
return peringkas.ringkas(p, d)
}
}
// === Demo inline ===
// Sebelum: helper `apakahLokal` dipakai 1x
fun prosesDenganInline(pelanggan: String): String {
val lokal = pelanggan.endsWith("@example.com")
return if (lokal) "Lokal" else "Eksternal"
}
fun main() {
println("=== Demo Refactoring ===")
val p = Pesanan(101, listOf("Buku", "Pulpen", "Penggaris"), "Budi", 120_000.0)
println("--- Sebelum refactoring ---")
println(prosesSebelum(p))
println()
println("--- Setelah refactoring ---")
val pemroses = PemrosesPesanan()
println(pemroses.proses(p))
println()
println("--- Kasus error (pelanggan kosong) ---")
val pKosong = p.copy(pelanggan = "")
println(pemroses.proses(pKosong))
println()
println("--- Inline function demo ---")
println(prosesDenganInline("[email protected]"))
println(prosesDenganInline("[email protected]"))
}Output yang Dihasilkan:
=== Demo Refactoring ===
--- Sebelum refactoring ---
Pesanan #101 oleh Budi: 3 item, total Rp120000.0, diskon Rp12000.0, bayar Rp108000.0
--- Setelah refactoring ---
Pesanan #101 oleh Budi: 3 item, total Rp120000.0, diskon Rp12000.0, bayar Rp108000.0
--- Kasus error (pelanggan kosong) ---
Error: pelanggan kosong
--- Inline function demo ---
Lokal
EksternalExtract Method #
Pada kode “sebelum”, method prosesSebelum melakukan tiga hal: validasi, hitung diskon, format output. Setelah Extract Method, kita mendapat tiga method terpisah. Keuntungannya:
- Testability: setiap method dapat di-test terpisah.
- Readability: nama method menggantikan komentar.
- Reusabilitas: method dapat dipakai ulang di tempat lain.
Extract Class #
Extract Class lebih jauh lagi: tiga method dipindahkan ke tiga class terpisah dengan tanggung jawab tunggal. PemrosesPesanan menjadi koordinator yang menyuntikkan tiga dependency. Struktur ini sekaligus menerapkan Dependency Injection — yang akan kita pelajari di section software design.
Inline #
Inline adalah kebalikan Extract: bila sebuah method hanya dipanggil satu kali atau sangat sederhana, inline dapat menyederhanakan kode. IntelliJ memiliki operasi Inline (CTRL+ALT+N) yang aman karena menganalisis semua pemanggilan.
Kapan Melakukan Refactoring #
Refactoring bukan aktivitas terpisah; ia menyatu dalam development sehari-hari. Beberapa pemicu:
- Sebelum menambah fitur: bila kode yang akan dimodifikasi sulit dipahami, refactor dulu sebelum menambah fitur.
- Saat memperbaiki bug: bug sering muncul di kode yang sulit dibaca; perbaiki bug, lalu refactor.
- Code review: reviewer menyarankan penyederhanaan; lakukan refactor sebelum merge.
- TDD red-green-refactor: setelah test lulus (green), lakukan refactor sebagai langkah ketiga siklus TDD.
Referensi Lanjutan #
- Diagnostics — panduan resmi diagnostics kompilator sebagai panduan refactor
- Coding conventions — konvensi kode Kotlin sebagai acuan refactor
Penutup #
Refactoring adalah keterampilan yang berkembang dengan latihan. IntelliJ memberikan alat untuk refactor dengan aman, tetapi keputusan kapan dan bagaimana tetap di tangan kita. Pada chapter berikutnya kita akan membahas code formatting: konvensi style yang membuat kode konsisten di seluruh tim.