Saya telah membaca Refactoring Martin Fowler . Ini umumnya sangat baik tetapi salah satu rekomendasi Fowler tampaknya menyebabkan sedikit masalah.
Fowler menyarankan Anda mengganti variabel sementara dengan kueri, jadi alih-alih ini:
double getPrice() {
final int basePrice = _quantity * _itemPrice;
final double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}
Anda menarik ke dalam metode pembantu:
double basePrice() {
return _quantity * _itemPrice;
}
double getPrice() {
final double discountFactor;
if (basePrice() > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice() * discountFactor;
}
Secara umum saya setuju kecuali bahwa salah satu alasan saya menggunakan variabel sementara adalah ketika sebuah garis terlalu panjang. Sebagai contoh:
$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);
Jika saya mencoba menguraikan itu, garis akan lebih panjang dari 80 karakter.
Bergantian saya berakhir dengan rantai kode, yang sendiri tidak begitu mudah dibaca:
$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));
Apa sajakah strategi untuk mendamaikan keduanya?
coding-style
refactoring
variables
Kevin Burke
sumber
sumber
$host
dan$uri
contoh dibuat-buat - kecuali tuan rumah sedang dibaca dari pengaturan atau input lain, saya lebih suka mereka berada di baris yang sama, bahkan jika itu membungkus atau pergi dari tepi.Jawaban:
Cara
1. Pembatasan panjang garis ada sehingga Anda dapat melihat + memahami lebih banyak kode. Mereka masih valid.
2. Tekankan penilaian atas konvensi buta .
3. Hindari variabel temp kecuali mengoptimalkan kinerja .
4. Hindari menggunakan indentasi yang dalam untuk penyelarasan dalam pernyataan multi-baris.
5. Pisahkan pernyataan panjang menjadi beberapa baris di sepanjang batas gagasan :
Reasoning
Sumber utama masalah (debugging) saya dengan variabel temp adalah bahwa mereka cenderung bisa berubah. Yaitu, saya akan menganggap mereka adalah satu nilai ketika saya menulis kode, tetapi jika fungsinya kompleks, beberapa bagian kode lainnya mengubah statusnya di tengah jalan. (Atau sebaliknya, di mana keadaan variabel tetap sama tetapi hasil kueri telah berubah).
Pertimbangkan bertahan dengan kueri kecuali Anda mengoptimalkan kinerja . Ini membuat logika apa pun yang Anda gunakan untuk menghitung nilai itu di satu tempat.
Contoh yang Anda berikan (Java dan ... PHP?) Keduanya memungkinkan untuk pernyataan multi-baris. Jika garisnya panjang, hancurkan. Sumber jquery membawa ini ke ekstrem. (Pernyataan pertama berjalan ke baris 69!) Bukan berarti saya setuju, tetapi ada cara lain untuk membuat kode Anda dapat dibaca daripada menggunakan temp vars.
Beberapa Contoh
1. Panduan Gaya PEP 8 untuk python (bukan contoh tercantik)
2. Paul M Jones pada Panduan Gaya Pear (tengah argumen jalan)
3. Panjang jalur Oracle + konvensi pembungkus (strategi yang berguna untuk menjaga hingga 80 karakter)
4. Praktek-praktek MDN Java (menekankan penilaian programmer atas konvensi)
sumber
Saya pikir, argumen terbaik untuk menggunakan metode pembantu bukan variabel sementara adalah keterbacaan manusia. Jika Anda, sebagai manusia, memiliki lebih banyak masalah dalam membaca rantai-metode-penolong daripada varialbe sementara, saya tidak melihat alasan mengapa Anda harus mengekstraknya.
(tolong koreksi saya jika saya salah)
sumber
Saya tidak berpikir Anda harus benar-benar mengikuti 80 pedoman karakter atau bahwa variabel temp lokal harus diekstraksi. Tetapi antrean panjang dan temps lokal harus diselidiki untuk cara yang lebih baik untuk mengekspresikan ide yang sama. Pada dasarnya, mereka mengindikasikan bahwa fungsi atau garis yang diberikan terlalu rumit dan kita perlu memecahnya. Tetapi kita perlu berhati-hati, karena memecah tugas menjadi beberapa bagian dengan cara yang buruk hanya membuat situasi lebih rumit. Jadi saya akan memecah hal-hal menjadi komponen resuable dan sederhana.
Biarkan saya melihat contoh yang Anda posting.
Pengamatan saya adalah bahwa semua panggilan twilio api akan dimulai dengan "https://api.twilio.com/2010-04-1/", dan dengan demikian ada fungsi yang dapat digunakan kembali yang sangat jelas:
Bahkan, saya pikir satu-satunya alasan untuk menghasilkan URL adalah untuk membuat permintaan, jadi saya akan lakukan:
Sebenarnya, banyak url yang sebenarnya dimulai dengan "Akun / $ akunSid", jadi saya mungkin akan mengekstraknya juga:
Dan jika kita menjadikan api twilio sebagai objek yang menyimpan nomor akun, kita dapat melakukan sesuatu seperti:
Menggunakan objek $ twilio memiliki manfaat membuat pengujian unit lebih mudah. Saya dapat memberikan objek objek $ twilio berbeda yang tidak benar-benar memanggil kembali ke twilio, yang akan lebih cepat dan tidak akan melakukan hal-hal aneh untuk twilio.
Mari kita lihat yang lain
Di sini saya akan memikirkan:
atau
atau
Tergantung pada yang merupakan idiom yang lebih dapat digunakan kembali.
sumber
Sebenarnya, saya tidak setuju dengan Pak Fowler yang terkemuka dalam hal ini dalam kasus umum.
Keuntungan mengekstraksi metode dari kode yang sebelumnya diuraikan adalah penggunaan kembali kode; kode dalam metode sekarang terputus dari penggunaan awalnya, dan sekarang dapat digunakan di tempat lain dalam kode tanpa disalin dan ditempelkan (yang mengharuskan perubahan di banyak tempat jika logika umum kode yang disalin harus diubah) .
Namun, dari nilai konseptual yang sama, sering lebih besar adalah "penggunaan kembali nilai". Mr. Fowler menyebut metode yang diekstrak ini untuk menggantikan variabel temp "queries". Nah, apa yang lebih efisien; meminta database setiap kali Anda membutuhkan nilai tertentu, atau meminta sekali dan menyimpan hasilnya (dengan asumsi bahwa nilai tersebut cukup statis sehingga Anda tidak akan mengharapkannya berubah)?
Untuk hampir semua perhitungan di luar yang relatif sepele dalam contoh Anda, dalam kebanyakan bahasa lebih murah untuk menyimpan hasil dari satu perhitungan daripada terus menghitungnya. Oleh karena itu, rekomendasi umum untuk menghitung ulang berdasarkan permintaan tidak jujur; biaya lebih banyak waktu pengembang dan lebih banyak waktu CPU, dan menghemat jumlah memori sepele, yang dalam kebanyakan sistem modern adalah sumber daya termurah dari ketiganya.
Sekarang, metode helper, bersama dengan kode lain, dapat dibuat "malas". Itu akan, ketika dijalankan pertama kali, menginisialisasi variabel. Semua panggilan lebih lanjut akan mengembalikan variabel itu sampai metode tersebut diberitahu secara eksplisit untuk melakukan perhitungan ulang. Ini bisa menjadi parameter untuk metode ini, atau tanda yang ditetapkan oleh kode lain yang mengubah nilai apa pun yang dihitung oleh metode ini:
Sekarang, untuk perhitungan sepele ini, itu bahkan lebih banyak pekerjaan yang dilakukan daripada disimpan, dan jadi saya biasanya merekomendasikan tetap dengan variabel temp; namun, untuk perhitungan yang lebih rumit yang biasanya ingin Anda hindari berjalan berulang kali, dan yang Anda perlukan di banyak tempat dalam kode, ini adalah cara Anda akan melakukannya.
sumber
Metode helper ada di tempat, tetapi Anda harus berhati-hati memastikan konsistensi data, dan peningkatan yang tidak perlu dalam lingkup variabel.
Misalnya, contoh Anda sendiri mengutip:
Jelas keduanya
_quantity
dan_itemPrice
merupakan variabel global (atau setidaknya tingkat kelas) dan oleh karena itu ada potensi bagi mereka untuk dimodifikasi di luargetPrice()
Karenanya ada potensi panggilan pertama
basePrice()
untuk mengembalikan nilai yang berbeda dari panggilan kedua!Oleh karena itu, saya akan menyarankan bahwa fungsi pembantu bisa berguna untuk mengisolasi matematika yang kompleks, tetapi sebagai pengganti variabel lokal, Anda harus berhati-hati.
Anda juga harus menghindari reductio ad absurdum - haruskah perhitungan
discountFactor
dihadiahkan ke suatu metode? Jadi contoh Anda menjadi:Partisi di luar level tertentu sebenarnya membuat kode kurang dibaca.
sumber
Jika Anda kebetulan bekerja dalam bahasa dengan parameter bernama (ObjectiveC, Python, Ruby, dll), vars temp kurang berguna.
Namun, dalam contoh basePrice Anda, kueri mungkin perlu beberapa saat untuk dieksekusi dan Anda mungkin ingin menyimpan hasilnya dalam variabel temp untuk digunakan di masa mendatang.
Seperti Anda, saya menggunakan variabel temp untuk pertimbangan kejelasan dan panjang garis.
Saya juga melihat programmer melakukan hal berikut dalam PHP. Ini menarik dan bagus untuk debugging, tapi agak aneh.
sumber
Alasan di balik rekomendasi ini adalah bahwa Anda ingin dapat menggunakan perhitungan yang sama di tempat lain dalam aplikasi Anda. Lihat Ganti Temp dengan Query di katalog pola refactoring:
Jadi, dalam contoh host dan URI Anda, saya hanya akan menerapkan rekomendasi ini jika saya berencana untuk menggunakan kembali definisi URI atau host yang sama.
Jika ini masalahnya, karena namespace, saya tidak akan mendefinisikan metode global uri () atau host (), tetapi nama dengan informasi lebih lanjut, seperti twilio_host (), atau archive_request_uri ().
Lalu, untuk masalah panjang garis, saya melihat beberapa opsi:
uri = archive_request_uri()
.Dasar Pemikiran: Dalam metode saat ini, Anda ingin URI menjadi yang disebutkan. Definisi URI masih difaktorkan.
uri() { return archive_request_uri() }
Jika Anda sering menggunakan rekomendasi Fowler, Anda akan tahu bahwa metode uri () adalah pola yang sama.
Jika karena pilihan bahasa Anda perlu mengakses metode lokal dengan 'mandiri', saya akan merekomendasikan solusi pertama, untuk meningkatkan ekspresif (dengan Python, saya akan mendefinisikan fungsi uri di dalam metode saat ini).
sumber