Menurut Martin Fowler , kode refactoring adalah (penekanan milikku):
Refactoring adalah teknik disiplin untuk merestrukturisasi tubuh kode yang ada, mengubah struktur internalnya tanpa mengubah perilaku eksternalnya . Jantungnya adalah serangkaian transformasi pelestarian perilaku kecil. Setiap transformasi (disebut 'refactoring') tidak banyak berpengaruh, tetapi serangkaian transformasi dapat menghasilkan restrukturisasi yang signifikan. Karena setiap refactoring kecil, itu cenderung salah. Sistem ini juga tetap berfungsi penuh setelah setiap refactoring kecil, mengurangi kemungkinan bahwa sistem dapat rusak parah selama restrukturisasi.
Apa itu "perilaku eksternal" dalam konteks ini? Sebagai contoh, jika saya menerapkan metode pindah refactoring dan memindahkan beberapa metode ke kelas lain, sepertinya saya mengubah perilaku eksternal, bukan?
Jadi, saya tertarik untuk mencari tahu pada titik mana perubahan berhenti menjadi refactor dan menjadi sesuatu yang lebih. Istilah "refactoring" dapat disalahgunakan untuk perubahan yang lebih besar: apakah ada kata lain untuk itu?
Memperbarui. Banyak jawaban menarik tentang antarmuka, tetapi tidakkah memindahkan metode refactoring mengubah antarmuka?
sumber
Jawaban:
"Eksternal" dalam konteks ini berarti "dapat diamati oleh pengguna". Pengguna mungkin manusia dalam hal aplikasi, atau program lain dalam kasus API publik.
Jadi, jika Anda memindahkan metode M dari kelas A ke kelas B, dan kedua kelas tersebut berada jauh di dalam suatu aplikasi, dan tidak ada pengguna yang dapat mengamati perubahan perilaku aplikasi karena perubahan tersebut, maka Anda dapat dengan tepat menyebutnya refactoring.
Jika, OTOH, beberapa subsistem / komponen tingkat tinggi lainnya mengubah perilakunya atau rusak karena perubahan, yang memang (biasanya) dapat diamati oleh pengguna (atau setidaknya sysadmin memeriksa log). Atau jika kelas Anda adalah bagian dari API publik, mungkin ada kode pihak ke-3 di luar sana yang bergantung pada M sebagai bagian dari kelas A, bukan B. Jadi, tidak satu pun dari kasus ini yang refactoring dalam arti yang ketat.
Memang, itu adalah konsekuensi yang menyedihkan tetapi diharapkan dari refactoring menjadi modis. Pengembang telah melakukan pengerjaan ulang kode secara ad hoc selama bertahun-tahun, dan tentu saja lebih mudah untuk mempelajari kata kunci baru daripada menganalisis dan mengubah kebiasaan yang sudah tertanam.
Saya akan menyebutnya desain ulang .
Memperbarui
Dari apa? Kelas spesifik, ya. Tetapi apakah kelas-kelas ini langsung terlihat oleh dunia luar? Jika tidak - karena mereka ada di dalam program Anda, dan bukan bagian dari antarmuka eksternal (API / GUI) dari program - tidak ada perubahan yang dibuat yang dapat diamati oleh pihak eksternal (kecuali perubahan itu menghancurkan sesuatu, tentu saja).
Saya merasa bahwa ada pertanyaan yang lebih dalam di luar ini: apakah kelas tertentu ada sebagai entitas independen dengan sendirinya? Dalam kebanyakan kasus, jawabannya adalah tidak : kelas hanya ada sebagai bagian dari komponen yang lebih besar, ekosistem kelas dan objek, tanpanya kelas tidak dapat dipakai dan / atau tidak dapat digunakan. Ekosistem ini tidak hanya mencakup dependensi (langsung / tidak langsung), tetapi juga kelas / objek lain yang bergantung padanya. Ini karena tanpa kelas tingkat yang lebih tinggi ini, tanggung jawab yang terkait dengan kelas kami mungkin tidak ada artinya / tidak berguna bagi pengguna sistem.
Misalnya dalam proyek kami yang berkaitan dengan penyewaan mobil, ada a
Charge
kelas. Kelas ini tidak digunakan oleh pengguna sistem itu sendiri, karena agen dan pelanggan stasiun penyewaan tidak dapat berbuat banyak dengan biaya individu: mereka berurusan dengan kontrak perjanjian sewa secara keseluruhan (yang mencakup banyak jenis biaya yang berbeda) . Para pengguna sebagian besar tertarik pada jumlah total biaya ini, bahwa mereka harus membayar pada akhirnya; agen tertarik pada opsi kontrak yang berbeda, panjang sewa, grup kendaraan, paket asuransi, item tambahan dll. dipilih, yang (melalui aturan bisnis canggih) mengatur biaya apa yang ada dan bagaimana pembayaran akhir dihitung dari ini. Dan perwakilan negara / analis bisnis peduli dengan aturan bisnis tertentu, sinergi dan efeknya (terhadap pendapatan perusahaan, dll.).Baru-baru ini saya refactored kelas ini, mengganti nama sebagian besar bidang dan metode (untuk mengikuti konvensi penamaan Java standar, yang benar-benar diabaikan oleh para pendahulu kami). Saya juga merencanakan refactoring lebih lanjut untuk mengganti
String
danchar
bidang dengan jenisenum
dan yang lebih tepatboolean
. Semua ini tentu saja akan mengubah antarmuka kelas, tetapi (jika saya melakukan pekerjaan saya dengan benar) tidak ada yang akan terlihat oleh pengguna aplikasi kami. Tak satu pun dari mereka yang peduli tentang bagaimana biaya individu yang diwakili, meskipun mereka pasti tahu konsep biaya. Saya bisa saja memilih sebagai contoh seratus kelas lain yang tidak mewakili konsep domain apa pun, jadi bahkan secara konsep tidak terlihat oleh pengguna akhir, tetapi saya pikir lebih menarik untuk mengambil contoh di mana setidaknya ada beberapa visibilitas pada tingkat konsep. Ini menunjukkan dengan baik bahwa antarmuka kelas hanya merupakan representasi dari konsep domain (paling-paling), bukan yang asli *. Representasi dapat diubah tanpa mempengaruhi konsep. Dan pengguna hanya memiliki dan memahami konsep; itu adalah tugas kita untuk melakukan pemetaan antara konsep dan representasi.* Dan kita dapat dengan mudah menambahkan bahwa model domain, yang diwakili oleh kelas kita, itu sendiri hanyalah representasi perkiraan dari beberapa "hal nyata" ...
sumber
Eksternal berarti antarmuka dalam makna bahasa sebenarnya. Pertimbangkan seekor sapi untuk contoh ini. Selama Anda memberi makan beberapa sayuran dan mendapatkan susu sebagai nilai pengembalian, Anda tidak peduli bagaimana organ internalnya bekerja. Sekarang jika Tuhan mengubah organ dalam sapi, sehingga darahnya menjadi berwarna biru, selama titik masuk dan titik keluar (mulut dan susu) tidak berubah, itu dapat dianggap sebagai refactoring.
sumber
Bagi saya, refactoring adalah yang paling produktif / nyaman ketika batas ditentukan oleh pengujian dan / atau dengan spesifikasi formal.
Batas-batas ini cukup kaku untuk membuat saya merasa aman mengetahui bahwa jika saya sesekali menyeberang, itu akan segera terdeteksi sehingga saya tidak perlu memutar balik banyak perubahan untuk pulih. Di sisi lain, ini memberikan kelonggaran yang cukup untuk meningkatkan kode tanpa khawatir mengubah perilaku yang tidak relevan.
Hal yang paling saya sukai adalah bahwa batas-batas semacam ini bersifat adaptif . Maksud saya, 1) Saya melakukan perubahan dan memverifikasi bahwa itu sesuai dengan spesifikasi / tes. Kemudian, 2) diteruskan ke QA atau pengujian pengguna - perhatikan di sini, mungkin masih gagal karena ada sesuatu yang hilang dalam spesifikasi / tes. OK, jika 3a) pengujian lulus, saya selesai, baiklah. Jika tidak, jika pengujian 3b) gagal maka saya 4) memutar balik perubahan dan 5) menambah tes atau mengklarifikasi spesifikasi sehingga lain kali kesalahan ini tidak akan berulang. Perhatikan bahwa tidak peduli apakah pengujian lulus atau gagal, saya mendapatkan sesuatu - baik kode / tes / spec ditingkatkan - usaha saya tidak berubah menjadi pemborosan total.
Adapun batas jenis lainnya - sejauh ini, saya tidak punya banyak keberuntungan.
"Dapat diamati oleh pengguna" adalah taruhan yang aman jika seseorang memiliki disiplin untuk mengikutinya - yang bagi saya entah bagaimana selalu melibatkan banyak upaya dalam menganalisis / membuat tes baru yang ada - mungkin terlalu banyak usaha. Hal lain yang saya tidak suka tentang pendekatan ini adalah bahwa mengikuti secara membabi buta mungkin berubah menjadi terlalu ketat. - Perubahan ini dilarang karena dengan itu memuat data akan memakan waktu 3 detik alih-alih 2. - Uhm baik bagaimana dengan memeriksa dengan pengguna / pakar UX apakah ini relevan atau tidak? - Tidak mungkin, perubahan dalam perilaku yang dapat diamati dilarang, titik. Aman? kamu bertaruh! produktif? tidak juga.
Satu lagi yang saya coba adalah menjaga logika kode (cara saya memahaminya saat membaca). Kecuali untuk perubahan yang paling dasar (dan biasanya tidak terlalu berbuah), yang ini selalu berupa kaleng cacing ... atau haruskah saya katakan sekaleng serangga? Maksud saya bug regresi. Terlalu mudah untuk memecahkan sesuatu yang penting ketika bekerja dengan kode spageti.
sumber
The Refactoring buku cukup kuat dalam pesannya bahwa Anda dapat hanya melakukan refactoring ketika Anda memiliki cakupan tes unit .
Dengan demikian, Anda dapat mengatakan bahwa selama Anda tidak melanggar salah satu dari tes unit Anda, Anda sedang melakukan refactoring . Ketika Anda memecahkan tes, Anda tidak refactoring lagi.
Tetapi: bagaimana dengan refactoring sederhana seperti mengubah nama kelas atau anggota? Bukankah mereka melanggar tes?
Ya, benar, dan dalam setiap kasus Anda harus mempertimbangkan apakah jeda itu penting. Jika SUT Anda adalah API / SDK publik, maka mengubah nama memang merupakan perubahan besar. Jika tidak, mungkin baik-baik saja.
Namun, pertimbangkan itu sering, tes istirahat bukan karena Anda mengubah perilaku yang benar-benar Anda pedulikan, tetapi karena tes tersebut adalah Tes Rapuh .
sumber
Cara terbaik untuk mendefinisikan "perilaku eksternal," dalam konteks ini, mungkin "menguji kasus."
Jika Anda refactor kode dan terus melewati test case (didefinisikan sebelum refactoring), maka refactoring tidak mengubah perilaku eksternal. Jika satu atau lebih kasus uji gagal, maka Anda telah mengubah perilaku eksternal.
Setidaknya, itulah pemahaman saya tentang berbagai buku yang diterbitkan pada topik (misalnya, Fowler).
sumber
Batasnya adalah garis yang memberitahu siapa yang mengembangkan, memelihara, mendukung proyek, dan mereka yang penggunanya selain pendukung, pengelola, pengembang. Jadi, ke dunia luar, perilaku itu terlihat sama sedangkan struktur internal di balik perilaku telah berubah.
Jadi seharusnya OK untuk memindahkan fungsi antar kelas asalkan bukan yang dilihat pengguna.
Selama pengerjaan ulang kode tidak mengubah perilaku eksternal, menambah fungsi baru atau menghapus fungsi yang ada, saya kira tidak apa-apa untuk menyebut pengerjaan ulang refactoring.
sumber
Dengan segala hormat, kita harus ingat bahwa pengguna kelas bukanlah pengguna akhir dari aplikasi yang dibangun dengan kelas, melainkan kelas yang diimplementasikan dengan memanfaatkan - baik panggilan atau warisan dari - kelas yang dire-refored .
Ketika Anda mengatakan bahwa "perilaku eksternal seharusnya tidak berubah" yang Anda maksud adalah sejauh menyangkut pengguna, kelas berperilaku persis seperti sebelumnya. Mungkin saja implementasi asli (yang tidak dire-refraksasikan) adalah satu kelas, dan implementasi yang baru (yang direfaktorasikan) memiliki satu atau lebih kelas-super yang menjadi dasar kelas tersebut dibangun, tetapi pengguna tidak pernah melihat bagian dalam (implementasi) yang mereka miliki. hanya melihat antarmuka.
Jadi jika kelas memiliki metode yang disebut "doSomethingAmazing" itu tidak masalah bagi pengguna jika itu diterapkan oleh kelas yang mereka maksudkan, atau oleh superclass di mana kelas itu dibangun. Semua yang penting bagi pengguna adalah bahwa "doSomethingAmazing" (refactored) yang baru memiliki hasil yang sama dengan yang lama (tidak direvokasi) "doSomethingAmazing.
Namun, apa yang disebut refactoring dalam banyak kasus bukanlah refactoring yang sebenarnya, tetapi mungkin reimplmentasi yang dilakukan untuk membuat kode lebih mudah dimodifikasi untuk menambahkan beberapa fitur baru. Jadi dalam kasus (pseudo) -refactoring nanti, kode (refactored) yang baru benar-benar melakukan sesuatu yang berbeda, atau mungkin sesuatu yang lebih dari yang lama.
sumber
Dengan "perilaku eksternal" terutama dia berbicara tentang antarmuka publik, tetapi ini juga mencakup output / artefak sistem juga. (mis. Anda memiliki metode yang menghasilkan file, mengubah format file akan mengubah perilaku eksternal)
e: Saya akan mempertimbangkan "metode pindah" perubahan ke perilaku eksternal. Ingatlah di sini bahwa Fowler berbicara tentang basis kode yang ada yang telah dilepaskan ke alam liar. Bergantung pada situasi Anda, Anda mungkin dapat memverifikasi bahwa perubahan Anda tidak menghancurkan klien eksternal dan melanjutkan dengan cara meriah.
e2: "Jadi apa kata yang tepat untuk pengerjaan ulang yang mengubah perilaku eksternal?" - API Refactoring, Breaking Change, dll ... masih refactoring, hanya saja tidak mengikuti praktik terbaik untuk refactoring antarmuka publik yang sudah di liar dengan klien.
sumber
"Metode bergerak" adalah teknik refactoring, bukan refactoring dengan sendirinya. Refactoring adalah proses penerapan beberapa teknik refactoring ke kelas. Di sana, ketika Anda mengatakan, saya menerapkan "metode pindah" ke kelas, Anda tidak benar-benar berarti "Saya refactored (kelas)", Anda sebenarnya berarti "Saya menerapkan teknik refactoring di kelas itu". Refactoring, di dalamnya makna paling murni diterapkan untuk desain, atau lebih spesifik, untuk beberapa bagian dari desain aplikasi yang dapat dilihat sebagai kotak hitam.
Anda bisa mengatakan bahwa "refactoring" yang digunakan dalam konteks kelas, berarti "teknik refactoring", dengan demikian "metode bergerak" tidak melanggar definisi proses refactoring. Pada halaman yang sama, "refactoring" dalam konteks desain, tidak merusak fitur yang ada dalam kode, itu hanya "merusak" desain (yang memang tujuannya).
Kesimpulannya, "batas", yang disebutkan dalam pertanyaan, dilewati jika Anda bingung (campuran: D) teknik refactoring-dengan-proses refactoring.
PS: Pertanyaan bagus btw.
sumber
jika Anda berbicara tentang angka anjak piutang, maka Anda menggambarkan kelompok bilangan bulat yang bila dikalikan sama dengan angka awal. Jika kita mengambil definisi ini untuk anjak piutang, dan menerapkannya pada refactor istilah pemrograman, maka refactoring akan memecah sebuah program menjadi unit logis terkecil, sehingga ketika mereka dijalankan sebagai sebuah program, menghasilkan output yang sama (diberi input yang sama ) sebagai program asli.
sumber
Batas-batas refactoring khusus untuk refactoring yang diberikan. Tidak mungkin ada satu jawaban dan mencakup segalanya. Salah satu alasannya adalah istilah refactoring itu sendiri tidak spesifik.
Anda dapat refactor:
dan saya yakin beberapa hal lainnya.
Mungkin refactor dapat didefinisikan sebagai
sumber
Pasti ada beberapa batasan pada seberapa jauh Anda bisa refactor. Lihat: Kapan Dua Algoritma Sama?
Jadi, jangan melangkah terlalu jauh, atau Anda tidak akan memiliki kepercayaan diri pada hasilnya. Di sisi lain, pengalaman menentukan bahwa Anda sering dapat mengganti satu algoritma dengan yang lain, dan mendapatkan jawaban yang sama, terkadang lebih cepat. Itulah keindahannya, eh?
sumber
Anda bisa memikirkan makna "eksternal"
Jadi setiap perubahan pada sistem yang tidak memengaruhi babi, dapat dianggap sebagai refactoring.
Mengubah antarmuka kelas bukan masalah, jika kelas hanya digunakan oleh sistem tunggal yang dibangun dan dikelola oleh tim Anda. Namun, jika kelas tersebut adalah kelas publik dalam kerangka .net yang digunakan oleh setiap programmer .net, ini adalah masalah yang sangat berbeda.
sumber