Seberapa jauh saya bisa mendorong refactoring tanpa mengubah perilaku eksternal?


27

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?


Jika perilaku yang ada menyebalkan atau tidak lengkap, kemudian mengubahnya, hapus / tulis ulang. Anda mungkin tidak mempertimbangkan ulang, tetapi siapa yang peduli apa namanya jika sistem akan (kanan?) Menjadi lebih baik sebagai hasilnya.
Pekerjaan

2
Atasan Anda mungkin peduli jika Anda diberi izin untuk refactor dan Anda menulis ulang.
JeffO

2
Batas refactoring adalah unit test. Jika Anda memiliki spesifikasi yang dibuat oleh mereka, apa pun yang Anda ubah yang tidak melanggar tes adalah refactoring?
George Silva


Jika Anda ingin belajar lebih banyak, topik ini juga merupakan bidang penelitian aktif. Ada banyak publikasi ilmiah tentang topik ini, misalnya, informatik.uni-trier.de/~ley/db/indices/a-tree/s/…
Skarab

Jawaban:


25

"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.

ada kecenderungan untuk menyebut kode ulang sebagai refactoring yang, saya kira, salah.

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.

Jadi apa kata yang tepat untuk pengerjaan ulang yang mengubah perilaku eksternal?

Saya akan menyebutnya desain ulang .

Memperbarui

Banyak jawaban menarik tentang antarmuka, tetapi tidakkah memindahkan metode refactoring mengubah antarmuka?

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 Chargekelas. 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 Stringdan charbidang dengan jenis enumdan yang lebih tepat boolean. 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" ...


3
nitpicking, saya akan mengatakan 'perubahan desain' tidak didesain ulang. mendesain ulang terdengar terlalu besar.
user606723

4
menarik - dalam contoh Anda, kelas A adalah 'badan kode yang ada ", dan jika metode M adalah publik maka perilaku eksternal A sedang diubah. Jadi, Anda mungkin bisa mengatakan bahwa kelas A dirancang ulang, sedangkan sistem keseluruhan sedang direactored.
sosis

Saya suka diamati oleh pengguna. Itu sebabnya saya tidak akan mengatakan tes unit melanggar adalah sebuah pertanda, tetapi tes ujung ke ujung atau integrasi akan menjadi sebuah pertanda.
Andy Wiesendanger

8

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.


1
Saya pikir itu bukan jawaban yang bagus. Refactoring dapat menyiratkan perubahan API. Tetapi seharusnya tidak mempengaruhi pengguna akhir atau cara input / file dibaca / dibuat.
rds

3

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.


3

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 .


3

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).


2

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.


Tidak setuju. Jika Anda mengganti seluruh kode akses data Anda dengan nHibernate, itu tidak mengubah perilaku eksternal tetapi tidak mengikuti "teknik disiplin" Fowler. Ini akan menjadi rekayasa ulang dan menyebutnya refactoring menyembunyikan faktor risiko yang terlibat.
pdr

1

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.


Bagaimana jika formulir windows digunakan untuk memunculkan dialog "Apakah Anda yakin ingin menekan tombol OK?" dan saya memutuskan untuk menghapusnya karena hanya menghasilkan sedikit hal baik dan mengganggu pengguna, lalu apakah saya telah memfaktorkan ulang kodenya, mendesain ulangnya, mengubahnya, menghapusnya, lainnya?
Pekerjaan

@job: Anda telah mengubah program untuk memenuhi spesifikasi baru.
jmoreno

IMHO Anda mungkin mencampur kriteria yang berbeda di sini. Menulis ulang kode dari awal memang bukan refactoring, tetapi hal ini terjadi terlepas dari apakah itu mengubah perilaku eksternal atau tidak. Juga, jika mengubah antarmuka kelas tidak refactoring, mengapa Move Method et al. ada di katalog refactorings ?
Péter Török

@ Péter Török - Tergantung sepenuhnya pada apa yang Anda maksud dengan "mengubah antarmuka kelas" karena dalam bahasa OOP yang mengimplementasikan warisan, antarmuka kelas tidak hanya mencakup apa yang diterapkan oleh kelas itu sendiri oleh semua leluhurnya. Mengubah antarmuka kelas berarti menghapus / menambahkan metode ke antarmuka (atau mengubah tanda tangan metode - yaitu nomor jenis parameter yang dilewati). Refactoring berarti siapa yang merespons metode, kelas atau superclass.
Zeke Hansell

IMHO - seluruh pertanyaan ini mungkin terlalu esoteris sehingga tidak bernilai bagi pemrogram.
Zeke Hansell

1

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.


Tetapi jika dia berbicara tentang antarmuka publik, bagaimana dengan refactoring "Move method"?
SiberianGuy

@Idsa Mengedit respons saya untuk menjawab pertanyaan Anda.

@kekela, tetapi masih belum jelas bagi saya di mana "refactoring" ini berakhir
SiberianGuy

@ idsa Menurut definisi yang Anda poskan, itu tidak lagi menjadi refactoring begitu Anda mengubah antarmuka publik. (memindahkan metode publik dari satu kelas ke kelas lain akan menjadi contohnya)

0

"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.


Bacalah beberapa kali, tetapi masih belum mendapatkannya
SiberianGuy

bagian apa yang tidak kamu mengerti? (ada refactoring-the-proses yang definisi Anda berlaku atau refactoring-the-teknik, dalam contoh Anda, metode pindah, yang definisi tidak berlaku; karenanya, metode pindah tidak melanggar definisi refactoring-the- proses, atau tidak melewati batasnya, apa pun itu). Saya katakan kekhawatiran Anda seharusnya tidak ada untuk contoh Anda. batas refactoring bukanlah sesuatu yang kabur. Anda hanya menerapkan definisi sesuatu untuk sesuatu yang lain.
Belun

0

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.


0

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:

  • Kode sumber
  • Arsitektur Program
  • Desain UI / Interaksi Pengguna

dan saya yakin beberapa hal lainnya.

Mungkin refactor dapat didefinisikan sebagai

"tindakan atau serangkaian tindakan yang mengurangi entropi dalam sistem tertentu"


0

Pasti ada beberapa batasan pada seberapa jauh Anda bisa refactor. Lihat: Kapan Dua Algoritma Sama?

Orang biasanya menganggap algoritma lebih abstrak daripada program yang mengimplementasikannya. Cara alami untuk memformalkan ide ini adalah bahwa algoritma adalah kelas program ekivalensi sehubungan dengan hubungan ekivalensi yang sesuai. Kami berpendapat bahwa tidak ada hubungan kesetaraan seperti itu ada.

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?


0

Anda bisa memikirkan makna "eksternal"

Di luar dari berdiri sehari-hari.

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.