Saat Anda mempelajari cara memprogram, Anda menghabiskan banyak waktu untuk menulis kode. Sebagian besar pengembang pemula percaya bahwa inilah yang akan mereka lakukan di masa depan. Ini sebagian benar, tetapi pekerjaan seorang programmer juga mencakup pemeliharaan dan pemfaktoran ulang kode. Hari ini kita akan berbicara tentang refactoring.
Memfaktorkan ulang pada CodeGym
Refactoring dibahas dua kali dalam kursus CodeGym:- Tugas besar di Level 5 dari pencarian Multithreading
- Pelajaran tentang refactoring di IntelliJ IDEA pada Level 9 dari pencarian Java Collections.
Apa itu refactoring?
Itu mengubah struktur kode tanpa mengubah fungsinya. Sebagai contoh, misalkan kita memiliki metode yang membandingkan 2 angka dan mengembalikan benar jika yang pertama lebih besar dan sebaliknya salah :public boolean max(int a, int b) {
if(a > b) {
return true;
} else if (a == b) {
return false;
} else {
return false;
}
}
Ini adalah kode yang agak berat. Bahkan pemula jarang menulis sesuatu seperti ini, tetapi ada peluang. Mengapa menggunakan if-else
blok jika Anda dapat menulis metode 6 baris dengan lebih ringkas?
public boolean max(int a, int b) {
return a > b;
}
Sekarang kami memiliki metode sederhana dan elegan yang melakukan operasi yang sama seperti contoh di atas. Beginilah cara kerja refactoring: Anda mengubah struktur kode tanpa memengaruhi esensinya. Ada banyak metode dan teknik refactoring yang akan kita lihat lebih dekat.
Mengapa Anda perlu melakukan refactoring?
Ada beberapa alasan. Misalnya, untuk mencapai kesederhanaan dan singkatnya kode. Pendukung teori ini percaya bahwa kode harus seringkas mungkin, bahkan jika diperlukan beberapa lusin baris komentar untuk memahaminya. Pengembang lain yakin bahwa kode harus difaktorkan ulang agar dapat dimengerti dengan jumlah komentar minimum. Setiap tim mengadopsi posisinya sendiri, tetapi ingat bahwa refactoring tidak berarti pengurangan . Tujuan utamanya adalah untuk memperbaiki struktur kode. Beberapa tugas dapat dimasukkan dalam tujuan keseluruhan ini:- Refactoring meningkatkan pemahaman kode yang ditulis oleh pengembang lain.
- Ini membantu menemukan dan memperbaiki bug.
- Itu dapat mempercepat kecepatan pengembangan perangkat lunak.
- Secara keseluruhan, ini meningkatkan desain perangkat lunak.
"Bau kode"
Ketika kode membutuhkan refactoring, dikatakan memiliki "bau". Tentu saja, tidak secara harfiah, tetapi kode seperti itu benar-benar tidak terlihat menarik. Di bawah ini kita akan mengeksplorasi teknik refactoring dasar untuk tahap awal.Kelas dan metode yang terlalu besar
Kelas dan metode bisa rumit, tidak mungkin untuk dikerjakan secara efektif justru karena ukurannya yang besar.Kelas besar
Kelas seperti itu memiliki banyak sekali baris kode dan banyak metode berbeda. Biasanya lebih mudah bagi pengembang untuk menambahkan fitur ke kelas yang sudah ada daripada membuat yang baru, itulah sebabnya kelas tumbuh. Biasanya, terlalu banyak fungsi yang dijejalkan ke dalam kelas seperti itu. Dalam hal ini, ada baiknya memindahkan sebagian fungsionalitas ke kelas terpisah. Kami akan membicarakan hal ini lebih detail di bagian teknik refactoring.Metode panjang
"Bau" ini muncul ketika pengembang menambahkan fungsionalitas baru ke suatu metode: "Mengapa saya harus memasukkan pemeriksaan parameter ke dalam metode terpisah jika saya dapat menulis kode di sini?", "Mengapa saya memerlukan metode pencarian terpisah untuk menemukan yang maksimal elemen dalam array? Mari kita simpan di sini. Kode akan lebih jelas dengan cara ini", dan kesalahpahaman lainnya.Ada dua aturan untuk memfaktorkan ulang metode yang panjang:
- Jika Anda ingin menambahkan komentar saat menulis metode, Anda harus meletakkan fungsionalitas tersebut dalam metode terpisah.
- Jika suatu metode membutuhkan lebih dari 10-15 baris kode, Anda harus mengidentifikasi tugas dan subtugas yang dijalankannya dan mencoba menempatkan subtugas ke dalam metode terpisah.
Ada beberapa cara untuk menghilangkan metode panjang:
- Pindahkan sebagian fungsionalitas metode ke dalam metode terpisah
- Jika variabel lokal mencegah Anda memindahkan sebagian fungsionalitas, Anda dapat memindahkan seluruh objek ke metode lain.
Menggunakan banyak tipe data primitif
Masalah ini biasanya terjadi ketika jumlah bidang di kelas bertambah dari waktu ke waktu. Misalnya, jika Anda menyimpan semuanya (mata uang, tanggal, nomor telepon, dll.) dalam tipe primitif atau konstanta, bukan objek kecil. Dalam hal ini, praktik yang baik adalah memindahkan pengelompokan bidang secara logis ke dalam kelas terpisah (kelas ekstrak). Anda juga dapat menambahkan metode ke kelas untuk memproses data.Terlalu banyak parameter
Ini adalah kesalahan yang cukup umum, terutama jika dikombinasikan dengan metode yang panjang. Biasanya, ini terjadi jika sebuah metode memiliki terlalu banyak fungsionalitas, atau jika sebuah metode mengimplementasikan beberapa algoritma. Daftar parameter yang panjang sangat sulit untuk dipahami, dan menggunakan metode dengan daftar seperti itu tidak nyaman. Akibatnya, lebih baik melewatkan seluruh objek. Jika objek tidak memiliki cukup data, Anda harus menggunakan objek yang lebih umum atau membagi fungsionalitas metode sehingga setiap metode memproses data yang terkait secara logis.Grup data
Grup data yang terkait secara logis sering muncul dalam kode. Misalnya, parameter koneksi basis data (URL, nama pengguna, kata sandi, nama skema, dll.). Jika tidak ada satu bidang pun yang dapat dihapus dari daftar bidang, maka bidang ini harus dipindahkan ke kelas terpisah (kelas ekstrak).Solusi yang melanggar prinsip OOP
"Bau" ini terjadi saat pengembang melanggar desain OOP yang tepat. Ini terjadi ketika dia tidak sepenuhnya memahami kemampuan OOP dan gagal menggunakannya secara penuh atau benar.Gagal menggunakan warisan
Jika subclass hanya menggunakan subset kecil dari fungsi kelas induk, maka itu berbau hirarki yang salah. Ketika ini terjadi, biasanya metode yang berlebihan tidak diganti atau mereka membuang pengecualian. Satu kelas mewarisi yang lain menyiratkan bahwa kelas anak menggunakan hampir semua fungsionalitas kelas induk. Contoh hierarki yang benar: Contoh hierarki yang salah:Beralih pernyataan
Apa yang salah dengan sebuahswitch
pernyataan? Itu buruk ketika menjadi sangat kompleks. Masalah terkait adalah sejumlah besar if
pernyataan bersarang.
Kelas alternatif dengan antarmuka yang berbeda
Beberapa kelas melakukan hal yang sama, tetapi metode mereka memiliki nama yang berbeda.Bidang sementara
Jika sebuah kelas memiliki bidang sementara yang hanya diperlukan objek sesekali ketika nilainya ditetapkan, dan itu kosong atau, amit-amit,null
di sisa waktu, maka kodenya berbau. Ini adalah keputusan desain yang dipertanyakan.
Bau yang membuat modifikasi sulit
Bau ini lebih serius. Bau lain terutama mempersulit untuk memahami kode, tetapi ini mencegah Anda untuk memodifikasinya. Saat Anda mencoba memperkenalkan fitur baru apa pun, setengah dari pengembang berhenti, dan setengah lagi menjadi gila.Hirarki warisan paralel
Masalah ini memanifestasikan dirinya ketika subclassing kelas mengharuskan Anda untuk membuat subclass lain untuk kelas yang berbeda.Dependensi yang terdistribusi secara seragam
Setiap modifikasi mengharuskan Anda untuk mencari semua kegunaan (ketergantungan) kelas dan membuat banyak perubahan kecil. Satu perubahan — edit di banyak kelas.Pohon modifikasi yang rumit
Bau ini kebalikan dari yang sebelumnya: perubahan memengaruhi sejumlah besar metode dalam satu kelas. Sebagai aturan, kode semacam itu memiliki ketergantungan yang mengalir: mengubah satu metode mengharuskan Anda untuk memperbaiki sesuatu di metode lain, lalu di metode ketiga, dan seterusnya. Satu kelas — banyak perubahan."Bau sampah"
Kategori bau yang agak tidak menyenangkan yang menyebabkan sakit kepala. Tidak berguna, tidak perlu, kode lama. Untungnya, IDE dan linter modern telah belajar untuk memperingatkan bau tersebut.Sejumlah besar komentar dalam suatu metode
Suatu metode memiliki banyak komentar penjelasan di hampir setiap baris. Hal ini biasanya disebabkan oleh algoritme yang rumit, jadi lebih baik membagi kode menjadi beberapa metode yang lebih kecil dan memberinya nama penjelas.Kode duplikat
Kelas atau metode yang berbeda menggunakan blok kode yang sama.Kelas malas
Kelas mengambil fungsi yang sangat sedikit, meskipun direncanakan untuk menjadi besar.Kode yang tidak digunakan
Kelas, metode, atau variabel tidak digunakan dalam kode dan merupakan bobot mati.Konektivitas yang berlebihan
Kategori bau ini dicirikan oleh banyaknya hubungan yang tidak dapat dibenarkan dalam kode.Metode eksternal
Metode menggunakan data dari objek lain jauh lebih sering daripada datanya sendiri.Keintiman yang tidak pantas
Kelas bergantung pada detail implementasi kelas lain.Panggilan kelas panjang
Satu kelas memanggil yang lain, yang meminta data dari yang ketiga, yang mendapatkan data dari yang keempat, dan seterusnya. Rantai panggilan yang begitu panjang berarti ketergantungan yang tinggi pada struktur kelas saat ini.Kelas pemberi tugas
Kelas diperlukan hanya untuk mengirim tugas ke kelas lain. Mungkin itu harus dihapus?Teknik pemfaktoran ulang
Di bawah ini kita akan membahas teknik refactoring dasar yang dapat membantu menghilangkan bau kode yang dijelaskan.Ekstrak kelas
Kelas melakukan terlalu banyak fungsi. Beberapa dari mereka harus dipindahkan ke kelas lain. Misalnya, kita memilikiHuman
kelas yang juga menyimpan alamat rumah dan memiliki metode yang mengembalikan alamat lengkap:
class Human {
private String name;
private String age;
private String country;
private String city;
private String street;
private String house;
private String quarter;
public String getFullAddress() {
StringBuilder result = new StringBuilder();
return result
.append(country)
.append(", ")
.append(city)
.append(", ")
.append(street)
.append(", ")
.append(house)
.append(" ")
.append(quarter).toString();
}
}
Merupakan praktik yang baik untuk menempatkan informasi alamat dan metode terkait (perilaku pemrosesan data) ke dalam kelas terpisah:
class Human {
private String name;
private String age;
private Address address;
private String getFullAddress() {
return address.getFullAddress();
}
}
class Address {
private String country;
private String city;
private String street;
private String house;
private String quarter;
public String getFullAddress() {
StringBuilder result = new StringBuilder();
return result
.append(country)
.append(", ")
.append(city)
.append(", ")
.append(street)
.append(", ")
.append(house)
.append(" ")
.append(quarter).toString();
}
}
Ekstrak metode
Jika suatu metode memiliki beberapa fungsionalitas yang dapat diisolasi, Anda harus menempatkannya dalam metode terpisah. Misalnya, metode yang menghitung akar persamaan kuadrat:public void calcQuadraticEq(double a, double b, double c) {
double D = b * b - 4 * a * c;
if (D > 0) {
double x1, x2;
x1 = (-b - Math.sqrt(D)) / (2 * a);
x2 = (-b + Math.sqrt(D)) / (2 * a);
System.out.println("x1 = " + x1 + ", x2 = " + x2);
}
else if (D == 0) {
double x;
x = -b / (2 * a);
System.out.println("x = " + x);
}
else {
System.out.println("Equation has no roots");
}
}
Kami menghitung masing-masing dari tiga opsi yang memungkinkan dalam metode terpisah:
public void calcQuadraticEq(double a, double b, double c) {
double D = b * b - 4 * a * c;
if (D > 0) {
dGreaterThanZero(a, b, D);
}
else if (D == 0) {
dEqualsZero(a, b);
}
else {
dLessThanZero();
}
}
public void dGreaterThanZero(double a, double b, double D) {
double x1, x2;
x1 = (-b - Math.sqrt(D)) / (2 * a);
x2 = (-b + Math.sqrt(D)) / (2 * a);
System.out.println("x1 = " + x1 + ", x2 = " + x2);
}
public void dEqualsZero(double a, double b) {
double x;
x = -b / (2 * a);
System.out.println("x = " + x);
}
public void dLessThanZero() {
System.out.println("Equation has no roots");
}
Setiap kode metode menjadi jauh lebih pendek dan lebih mudah dipahami.
Melewati seluruh objek
Saat metode dipanggil dengan parameter, terkadang Anda mungkin melihat kode seperti ini:public void employeeMethod(Employee employee) {
// Some actions
double yearlySalary = employee.getYearlySalary();
double awards = employee.getAwards();
double monthlySalary = getMonthlySalary(yearlySalary, awards);
// Continue processing
}
public double getMonthlySalary(double yearlySalary, double awards) {
return (yearlySalary + awards)/12;
}
Itu employeeMethod
memiliki 2 baris penuh yang dikhususkan untuk menerima nilai dan menyimpannya dalam variabel primitif. Terkadang konstruksi seperti itu bisa memakan waktu hingga 10 baris. Jauh lebih mudah untuk meneruskan objek itu sendiri dan menggunakannya untuk mengekstrak data yang diperlukan:
public void employeeMethod(Employee employee) {
// Some actions
double monthlySalary = getMonthlySalary(employee);
// Continue processing
}
public double getMonthlySalary(Employee employee) {
return (employee.getYearlySalary() + employee.getAwards())/12;
}
Sederhana, singkat, dan padat.
Mengelompokkan bidang secara logis dan memindahkannya menjadi terpisahclassDespite
fakta bahwa contoh di atas sangat sederhana, dan ketika Anda melihatnya, banyak dari Anda mungkin bertanya, "Siapa yang melakukan ini?", banyak pengembang membuat kesalahan struktural seperti itu karena kecerobohan, keengganan untuk memperbaiki kode, atau hanya sikap "itu cukup baik".
Mengapa refactoring efektif
Sebagai hasil dari pemfaktoran ulang yang baik, sebuah program memiliki kode yang mudah dibaca, prospek untuk mengubah logikanya tidak menakutkan, dan memperkenalkan fitur baru tidak menjadi neraka analisis kode, melainkan pengalaman yang menyenangkan selama beberapa hari. . Anda tidak boleh melakukan refactor jika akan lebih mudah untuk menulis program dari awal. Misalnya, tim Anda memperkirakan bahwa tenaga kerja yang diperlukan untuk memahami, menganalisis, dan memfaktorkan ulang kode akan lebih besar daripada mengimplementasikan fungsionalitas yang sama dari awal. Atau jika kode yang akan di-refactor memiliki banyak masalah yang sulit untuk di-debug. Mengetahui cara memperbaiki struktur kode sangat penting dalam pekerjaan seorang programmer. Dan belajar memprogram di Java paling baik dilakukan di CodeGym, kursus online yang menekankan praktik. 1200+ tugas dengan verifikasi instan, sekitar 20 proyek mini, tugas permainan — semua ini akan membantu Anda merasa percaya diri dalam pengkodean. Waktu terbaik untuk memulai adalah sekarang :)Sumber daya untuk membenamkan diri Anda lebih jauh dalam pemfaktoran ulang
Buku paling terkenal tentang refactoring adalah "Refactoring. Improving the Design of Existing Code" oleh Martin Fowler. Ada juga publikasi menarik tentang refactoring, berdasarkan buku sebelumnya: "Refactoring Using Patterns" oleh Joshua Kerievsky. Berbicara tentang pola... Saat melakukan refactoring, selalu sangat berguna untuk mengetahui pola desain dasar. Buku-buku bagus ini akan membantu dalam hal ini: Berbicara tentang pola... Saat melakukan pemfaktoran ulang, selalu sangat berguna untuk mengetahui pola desain dasar. Buku-buku bagus ini akan membantu dalam hal ini:- "Pola Desain" oleh Eric Freeman, Elizabeth Robson, Kathy Sierra, dan Bert Bates, dari seri Head First
- "Seni Kode yang Dapat Dibaca" oleh Dustin Boswell dan Trevor Foucher
- "Code Complete" oleh Steve McConnell, yang menetapkan prinsip untuk kode yang indah dan elegan.