Membagi perhitungan nilai balik dan pernyataan balik dalam metode satu baris?

26

Saya telah berdiskusi dengan rekan kerja tentang melanggar returnpernyataan dan pernyataan yang menghitung nilai balik dalam dua baris.

Sebagai contoh

private string GetFormattedValue()
{
    var formattedString = format != null ? string.Format(format, value) : value.ToString();
    return formattedString;
}

dari pada

private string GetFormattedValue()
{
    return format != null ? string.Format(format, value) : value.ToString();
}

Dari segi kode, saya tidak benar-benar melihat nilai pada varian pertama. Bagi saya, yang terakhir lebih jelas, terutama untuk metode yang pendek. Argumennya adalah bahwa varian sebelumnya lebih mudah untuk di-debug - yang merupakan prestasi yang sangat kecil, karena VisualStudio memungkinkan kita melakukan pemeriksaan yang sangat terperinci terhadap pernyataan, ketika eksekusi dihentikan karena titik istirahat.

Pertanyaan saya adalah, apakah masih ada gunanya menulis kode kurang jelas, hanya untuk membuat debug sekilas lebih mudah? Apakah ada argumen lebih lanjut untuk varian dengan perhitungan split dan returnpernyataan?

Paul Kertscher
sumber
18
Tidak bekerja pada VS, tetapi akan menganggap Anda tidak dapat menetapkan breakpoint bersyarat pada ekspresi yang rumit (atau itu akan rumit untuk masuk), jadi mungkin akan menempatkan menetapkan dan kembali ke pernyataan terpisah, hanya untuk kenyamanan. Kompiler kemungkinan besar akan muncul dengan kode yang sama untuk keduanya.
tofro
1
Ini mungkin tergantung pada bahasa, terutama dalam bahasa di mana variabel memiliki (mungkin kompleks) perilaku objek daripada perilaku pointer. Pernyataan @Paul K mungkin benar untuk bahasa dengan perilaku pointer, bahasa di mana objek memiliki perilaku nilai sederhana, dan bahasa dengan matang, kompiler berkualitas tinggi.
MSalters
4
"Karena VisualStudio memungkinkan kami melakukan pemeriksaan yang sangat rinci terhadap pernyataan, ketika eksekusi dihentikan karena titik istirahat" - begitu. Jadi, bagaimana Anda mendapatkan nilai kembali jika fungsi mengembalikan sebuah struct dengan lebih dari satu anggota? (dan dukungan untuk fitur itu paling tidak bagus, ada banyak kombinasi di mana Anda tidak mendapatkan nilai pengembalian sama sekali).
Voo
2
Bagaimana memiliki "pemeriksaan pernyataan yang sangat terperinci" di debuger yang digunakan seseorang HARI INI, menjadikannya pilihan yang buruk untuk menulis kode sehingga mudah untuk debug di debuger APAPUN?
Ian
16
Ganggu dia lebih lanjut dengan mengurangi seluruh fungsi tubuh menjadiprivate string GetFormattedValue() => string.Format(format ?? "{0}", value);
Graham

Jawaban:

46

Memperkenalkan variabel yang menjelaskan adalah refactoring terkenal yang terkadang dapat membantu membuat ekspresi rumit menjadi lebih mudah dibaca. Namun, dalam kasus yang ditunjukkan,

  • variabel tambahan tidak "menjelaskan" apa pun yang tidak jelas dari nama metode di sekitarnya
  • pernyataan itu menjadi lebih lama, jadi (sedikit) kurang bisa dibaca

Selain itu, versi yang lebih baru dari Visual Studio debugger dapat menunjukkan nilai balik suatu fungsi dalam banyak kasus tanpa memperkenalkan variabel yang berlebihan (tetapi berhati-hatilah, ada beberapa peringatan, lihat posting SO yang lebih tua ini dan jawaban yang berbeda ).

Jadi dalam kasus khusus ini, saya setuju untuk Anda, namun, ada kasus lain di mana variabel yang menjelaskan memang dapat meningkatkan kualitas kode.

Doc Brown
sumber
Saya setuju juga, bahwa pasti ada kasus di mana itu berguna, tidak diragukan lagi.
Paul Kertscher
2
Saya biasanya menggunakan resultnama variabel. Tidak terlalu lama dan lebih mudah untuk di-debug
edc65
26
@ edc65: nama generik seperti result sering hanya menambahkan derau ke kode dan jarang meningkatkan keterbacaan, yang merupakan inti dari jawaban saya. Ini dapat dibenarkan dalam konteks di mana ini membantu debugging, tapi saya akan menghindari ketika menggunakan debugger yang tidak memerlukan variabel terpisah.
Doc Brown
6
@JonHanna alat panjang menurut saya. Nama resultmenyampaikan informasi bahwa ini adalah nilai yang dihasilkan dari fungsi, sehingga Anda dapat melihatnya sebelum fungsi kembali.
edc65
1
@ edc65 tapi itu membuatnya terlihat bermanfaat. Jadi sekarang ketika saya membaca kode Anda, saya tidak segera menyadari itu bukan. Jadi kode Anda menjadi kurang terbaca.
Jon Hanna
38

Mengingat fakta bahwa:

a) Tidak ada dampak pada kode akhir karena kompiler mengoptimalkan variabel menjauh.

b) Memiliki terpisah meningkatkan kemampuan debugging.

Saya pribadi sampai pada kesimpulan bahwa ini adalah praktik yang baik untuk memisahkan mereka 99% dari waktu.

Tidak ada kerugian material untuk melakukannya dengan cara ini. Argumen bahwa itu kode kembung adalah keliru, karena kode kembung adalah masalah sepele dibandingkan dengan kode tidak dapat dibaca atau sulit untuk debug. Selain itu, metode ini tidak dapat dengan sendirinya membuat kode yang membingungkan, itu sepenuhnya terserah pengembang.

Dom
sumber
9
Ini jawaban yang tepat untukku. Ini membuatnya lebih mudah untuk menetapkan breakpoint dan melihat nilai saat debugging, dan tidak memiliki downside yang saya sadari.
Matthew James Briggs
Untuk titik b, dalam Visual Studio Code, cukup letakkan breakpoint di kembalinya dan kemudian tambahkan ekspresi: GetFormattedValue () dan itu akan menunjukkan hasil ketika breakpoint terkena, sehingga garis tambahan tidak diperlukan. Tetapi lebih mudah untuk melihat penduduk lokal dengan garis tambahan karena tidak perlu menambahkan ekspresi tambahan di debugger. Jadi, benar-benar masalah preferensi pribadi.
Jon Raynor
3
@JonRaynor untuk nilai kembali, letakkan breakpoint pada braket penutup fungsi. Itu menangkap nilai yang dikembalikan, bahkan dalam fungsi dengan beberapa pengembalian.
Baldrickk
16

Seringkali, memperkenalkan variabel hanya untuk memberi nama beberapa hasil sangat membantu ketika itu membuat kode lebih mendokumentasikan diri. Dalam hal ini itu bukan faktor karena nama variabel sangat mirip dengan nama metode.

Perhatikan bahwa satu metode garis tidak memiliki nilai bawaan. Jika perubahan memperkenalkan lebih banyak baris tetapi membuat kode lebih jelas, itu adalah perubahan yang baik.

Namun secara umum, keputusan ini sangat tergantung pada preferensi pribadi Anda. Misalnya, saya menemukan kedua solusi membingungkan karena operator kondisional digunakan secara tidak perlu. Saya lebih suka pernyataan jika. Tetapi di tim Anda, Anda mungkin telah menyetujui berbagai konvensi. Kemudian lakukan apa pun yang disarankan oleh konvensi Anda. Jika konvensi ini diam mengenai kasus seperti ini, perhatikan bahwa ini adalah perubahan yang sangat kecil yang tidak masalah dalam jangka panjang. Jika pola ini terjadi berulang kali, mungkin memulai diskusi bagaimana Anda sebagai tim ingin menangani kasus-kasus ini. Tapi itu memisahkan antara "kode yang baik" dan "mungkin kode yang sedikit lebih baik".

amon
sumber
1
"Saya menemukan kedua solusi ini membingungkan karena operator kondisional digunakan secara tidak perlu." - Ini bukan contoh dunia nyata, saya hanya harus membuat sesuatu, cepat Memang ini mungkin bukan contoh terbaik.
Paul Kertscher
4
+1 karena pada dasarnya mengatakan ini adalah perbedaan "di bawah radar" yang (tidak sama dengan hal lain) tidak layak untuk diganggu.
TripeHound
3
@Indwin, ketika saya menggunakan operator ternary, saya biasanya membaginya menjadi beberapa baris sehingga jelas apa yang benar dan apa yang salah.
Arturo Torres Sánchez
2
@ ArturoTorresSánchez Saya melakukan itu juga, tetapi alih-alih a ?dan :saya gunakan if() {dan } else {- - - - \\ :)
Mindwin
3
@Mindwin, tetapi saya tidak bisa melakukan itu ketika saya berada di tengah-tengah ekspresi (seperti inisialisasi objek)
Arturo Torres Sánchez
2

Menanggapi pertanyaan Anda:

Pertanyaan saya adalah, apakah masih ada gunanya menulis kode kurang jelas, hanya untuk membuat debug sekilas lebih mudah?

Iya nih. Bahkan, bagian dari pernyataan Anda sebelumnya bagi saya (jangan tersinggung) sedikit picik (lihat huruf tebal di bawah) " Argumennya adalah bahwa varian sebelumnya lebih mudah untuk di-debug - yang merupakan prestasi yang cukup kecil , karena VisualStudio memungkinkan kami memeriksa laporan dengan sangat rinci, ketika eksekusi dihentikan karena titik istirahat. "

Membuat debugging lebih mudah (hampir) tidak pernah " kecil " karena oleh beberapa perkiraan 50% dari waktu programmer dihabiskan untuk debugging ( Reversible Debugging Software ).

Apakah ada argumen lebih lanjut untuk varian dengan perhitungan split dan pernyataan pengembalian?

Iya nih. Beberapa pengembang berpendapat bahwa penghitungan split lebih mudah dibaca. Ini, tentu saja, membantu dengan debugging tetapi juga membantu ketika seseorang mencoba menguraikan aturan bisnis apa pun yang mungkin dilakukan atau diterapkan oleh kode Anda.

CATATAN: Aturan bisnis mungkin lebih baik disajikan dalam database karena mereka dapat sering berubah. Namun demikian, pengkodean yang jelas dalam bidang ini masih sangat penting. ( Cara Membangun Mesin Aturan Bisnis )

tale852150
sumber
1

Saya akan melangkah lebih jauh:

private string GetFormattedValue()
{
    if (format != null) {
        formattedString = string.Format(format, value);
    } else {
        formattedString = value.ToString()
    }
    return formattedString;
}

Mengapa?

Menggunakan operator ternary untuk logika yang lebih kompleks tidak dapat dibaca, jadi Anda akan menggunakan gaya seperti di atas untuk pernyataan yang lebih kompleks. Dengan selalu menggunakan gaya ini, kode Anda konsisten dan lebih mudah bagi manusia untuk diurai. Selain itu, dengan memperkenalkan jenis konsistensi (dan menggunakan kode serat dan tes lainnya) Anda dapat menghindari goto failkesalahan ketik.

Keuntungan lain adalah bahwa laporan cakupan kode Anda akan memberi tahu Anda jika Anda lupa menyertakan tes formatbukan nol. Ini tidak akan menjadi kasus bagi operator ternary.


Alternatif pilihan saya - jika Anda berada di "dapatkan pengembalian secepat mungkin" dan tidak menentang beberapa pengembalian dari metode:

private string GetFormattedValue()
{
    if (format != null) {
        return string.Format(format, value);
    }

    return value.ToString();
}

Jadi, Anda dapat melihat pengembalian terakhir untuk melihat apa defaultnya.

Penting untuk konsisten - dan mintalah semua metode Anda mengikuti satu atau beberapa konvensi lainnya.

HorusKol
sumber
1
Contoh pertama tampaknya praktik buruk karena value.ToString()dipanggil tidak perlu ketika formatnya bukan nol. Dalam kasus umum, ini bisa termasuk perhitungan non-sepele, dan dapat memakan waktu lebih lama dari versi termasuk string format. Pertimbangkan, misalnya, a valueyang menyimpan PI ke satu juta tempat desimal, dan format string yang hanya meminta beberapa digit pertama.
Steve
1
mengapa tidak private string GetFormattedValue() => string.Format(format ?? "{0}", value); sama mempengaruhi, dan menggunakan unit test untuk memastikan kebenaran alih-alih mengandalkan debugger.
Berin Loritsch
1
Sementara saya setuju terner sebuah dapat menjadi kurang jelas, null terminator dapat membuat hal-hal yang lebih jelas. Setidaknya dalam hal ini.
Berin Loritsch
1
Dear diary, hari ini saya telah membaca bahwa menulis kode yang jelas dan ringkas menggunakan paradigma, idiom, dan operator terkenal (sudah ada selama sekitar 40 tahun) adalah, kutipan, kutipan ganda menjadi kutipan ganda pintar, tanda kutip - tetapi alih-alih menulis kode verbose yang melanggar KERING dan tidak menggunakan operator, idiom, dan paradigma yang disebutkan di atas, alih-alih mencoba menghindari apa pun yang mungkin tampak samar hanya untuk anak berusia lima tahun tanpa latar belakang pemrograman sebelumnya - lebih jelas . Sial, aku pasti sudah benar-benar tua, buku harianku tersayang ... Aku seharusnya belajar Go ketika aku punya kesempatan.
vaxquis
1
"Menggunakan operator ternary untuk logika yang lebih kompleks tidak dapat dibaca" Meskipun memang demikian (dan saya telah melihat orang-orang berlebih logika rumit) ini tidak benar untuk kode OP, dan juga bukan hal khusus untuk operator ternary. Satu-satunya hal yang bisa saya katakan dengan keyakinan penuh adalah bahwa garisnya terlalu panjang. gist.github.com/milleniumbug/cf9b62cac32a07899378feef6c06c776 adalah cara saya memformat ulang.
milleniumbug
1

Saya tidak berpikir bahwa teknik seperti itu dapat dibenarkan oleh kebutuhan untuk debug. Saya telah mengalami pendekatan ini sendiri ribuan kali, dan dari waktu ke waktu saya terus melakukan ini, tetapi saya selalu ingat apa yang dikatakan Martin Fowler tentang debugging :

Orang-orang juga meremehkan waktu yang mereka habiskan untuk debugging. Mereka meremehkan berapa banyak waktu yang bisa mereka habiskan untuk mengejar bug yang panjang. Dengan pengujian, saya langsung tahu ketika saya menambahkan bug. Itu memungkinkan saya memperbaiki bug segera, sebelum dapat merangkak keluar dan bersembunyi. Ada beberapa hal yang lebih membuat frustrasi atau membuang-buang waktu daripada debugging. Bukankah itu akan menjadi jauh lebih cepat jika kita tidak membuat bug di tempat pertama?

Zapadlo
sumber
Martin Fowler adalah pria yang cerdas dan saya senang membaca pandangannya (dan Anda). Sementara saya sangat yakin bahwa pengujian diperlukan dan bahwa lebih banyak waktu harus dihabiskan dalam upaya itu, fakta bahwa kita semua adalah manusia yang keliru menunjukkan bahwa tidak ada jumlah pengujian yang akan memberantas semua bug. Karenanya, debugging akan selalu menjadi bagian dari pengembangan program dan proses dukungan.
tale852150
1

Saya pikir beberapa orang terpaku pada masalah yang berkaitan dengan pertanyaan, seperti operator ternary. Ya, banyak orang membencinya, jadi mungkin ada baiknya mengemukakannya.

Mengenai fokus pertanyaan Anda, memindahkan pernyataan kembali untuk direferensikan oleh variabel ...

Pertanyaan ini membuat 2 asumsi yang tidak saya setujui:

  1. Bahwa varian kedua lebih jelas atau mudah dibaca (saya katakan yang sebaliknya benar), dan

  2. bahwa setiap orang menggunakan Visual Studio. Saya telah menggunakan Visual Studio berkali-kali dan dapat menggunakannya dengan baik, tetapi saya biasanya menggunakan sesuatu yang lain. Lingkungan dev yang memaksa IDE spesifik adalah sesuatu yang saya akan skeptis.

Memecah sesuatu ke variabel bernama jarang membuat sesuatu lebih sulit untuk dibaca, hampir selalu melakukan yang sebaliknya. Spesifik cara di mana seseorang tidak dapat menyebabkan masalah, seperti jika tuan dokumentasi diri tidak var thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...maka jelas yang buruk, tapi itu adalah masalah yang terpisah. var formattedText = ...baik-baik saja.

Dalam kasus khusus ini, dan mungkin banyak kasus karena kita berbicara tentang 1-liner, variabel tidak akan banyak memberi tahu Anda bahwa nama fungsi belum memberi tahu Anda. Oleh karena itu, variabel tidak bertambah banyak. Argumen debugging masih bisa berlaku, tetapi sekali lagi, dalam kasus khusus ini saya tidak melihat apa pun yang mungkin menjadi fokus Anda ketika debugging, dan itu selalu dapat dengan mudah diubah nanti jika entah bagaimana seseorang membutuhkan format itu untuk debugging atau apa pun.

Secara umum, dan Anda memang meminta aturan umum (contoh Anda hanya itu, contoh bentuk umum), semua poin yang dibuat mendukung varian 1 (2-liner) benar. Itu adalah pedoman yang baik untuk dimiliki. Tetapi pedoman harus fleksibel. Misalnya, proyek yang sedang saya kerjakan sekarang memiliki 80 karakter per baris maksimum, jadi saya membagi banyak baris, tetapi saya biasanya menemukan garis 81-85 karakter yang akan canggung untuk memecah atau mengurangi keterbacaan dan saya membiarkannya selesai batasnya.

Karena tidak mungkin menambah nilai, saya tidak akan melakukan 2 baris untuk contoh spesifik yang diberikan. Saya akan melakukan varian 2 (1-liner) karena poin tidak cukup kuat untuk melakukan sebaliknya dalam kasus ini.

Harun
sumber