Perbaikan bug baru-baru ini mengharuskan saya membaca kode yang ditulis oleh anggota tim lain, tempat saya menemukan ini (ini C #):
return (decimal)CostIn > 0 && CostOut > 0 ? (((decimal)CostOut - (decimal)CostIn) / (decimal)CostOut) * 100 : 0;
Sekarang, membiarkan ada alasan yang bagus untuk semua pemain itu, ini sepertinya masih sangat sulit untuk diikuti. Ada bug kecil dalam perhitungan dan saya harus menguraikannya untuk memperbaiki masalah.
Saya tahu gaya pengkodean orang ini dari ulasan kode, dan pendekatannya adalah bahwa lebih pendek hampir selalu lebih baik. Dan tentu saja ada nilai di sana: kita semua telah melihat rantai logika kondisional kompleks yang tidak perlu yang dapat dirapikan dengan beberapa operator yang ditempatkan dengan baik. Tapi dia jelas lebih mahir daripada saya dalam mengikuti rantai operator yang dijejalkan ke dalam satu pernyataan.
Ini, tentu saja, pada akhirnya adalah masalah gaya. Tetapi apakah ada sesuatu yang ditulis atau diteliti tentang mengenali titik di mana perjuangan untuk kode singkat berhenti menjadi berguna dan menjadi penghalang bagi pemahaman?
Alasan para pemain adalah Entity Framework. Db perlu menyimpan ini sebagai tipe yang dapat dibatalkan. Desimal? tidak setara dengan Desimal dalam C # dan perlu dicetak.
sumber
CostOut
sama denganDouble.Epsilon
, dan karena itu lebih besar dari nol. Tetapi(decimal)CostOut
dalam hal ini nol, dan kami memiliki pembagian dengan kesalahan nol. Langkah pertama harus mendapatkan kode yang benar , yang saya pikir tidak. Dapatkan benar, buat test case, dan kemudian buat elegan . Kode elegan dan kode singkat memiliki banyak kesamaan, tetapi terkadang singkatnya bukanlah jiwa keanggunan.Jawaban:
Untuk menjawab pertanyaan Anda tentang penelitian yang masih ada
Ya, ada pekerjaan di bidang ini.
Untuk mendapatkan pemahaman tentang hal ini, Anda harus menemukan cara untuk menghitung metrik sehingga perbandingan dapat dilakukan secara kuantitatif (bukan hanya melakukan perbandingan berdasarkan kecerdasan dan intuisi, seperti jawaban lainnya). Satu metrik potensial yang telah dilihat adalah
Kompleksitas Siklomatik ÷ Source Lines of Code ( SLOC )
Dalam contoh kode Anda, rasio ini sangat tinggi, karena semuanya telah dikompresi menjadi satu baris.
Tautan
Berikut adalah beberapa referensi jika Anda tertarik:
McCabe, T. dan A. Watson (1994), Kompleksitas Perangkat Lunak (CrossTalk: Jurnal Rekayasa Perangkat Lunak Pertahanan).
Watson, AH, & McCabe, TJ (1996). Structured Testing: Suatu Metodologi Pengujian Menggunakan Metrik Kompleksitas Siklomatik (Publikasi Khusus NIST 500-235). Diperoleh 14 Mei 2011, dari situs web Perangkat Lunak McCabe: http://www.mccabe.com/pdf/mccabe-nist235r.pdf
Rosenberg, L., Hammer, T., Shaw, J. (1998). Metrik dan Keandalan Perangkat Lunak (Prosiding Simposium Internasional IEEE tentang Rekayasa Keandalan Perangkat Lunak). Diperoleh 14 Mei 2011, dari situs web Universitas Negeri Penn: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.104.4041&rep=rep1&type=pdf
Pendapat dan solusi saya
Secara pribadi, saya tidak pernah menghargai singkatnya, hanya keterbacaan. Terkadang singkat membantu kesiapan, kadang tidak. Yang lebih penting adalah Anda menulis Really Obvious Code (ROC) alih-alih Write-Only Code (WOC).
Hanya untuk bersenang-senang, inilah cara saya akan menulisnya, dan meminta anggota tim saya untuk menulisnya:
Perhatikan juga pengenalan variabel kerja memiliki efek samping yang menyenangkan dari memicu aritmatika titik tetap dan bukan aritmatika bilangan bulat, sehingga kebutuhan semua gips untuk
decimal
dihilangkan.sumber
if ((costIn < 0) || (costOut < 0)) throw new Exception("costs must not be negative");
Brevity baik ketika mengurangi kekacauan di sekitar hal-hal yang penting, tetapi ketika menjadi singkat , mengemas terlalu banyak data yang relevan terlalu padat untuk diikuti, maka data yang relevan menjadi berantakan sendiri dan Anda memiliki masalah.
Dalam kasus khusus ini, gips untuk
decimal
diulangi berulang kali; secara keseluruhan mungkin akan lebih baik untuk menulis ulang sebagai sesuatu seperti:Tiba-tiba garis yang berisi logika jauh lebih pendek dan pas pada satu garis horizontal, sehingga Anda dapat melihat semuanya tanpa harus menggulir, dan maknanya jauh lebih mudah terlihat.
sumber
((decOut - decIn ) / decOut) * 100
ke variabel lain.CostOut > 0
), jadi Anda harus memperluas kondisional ke dalam-if
pernyataan. Bukannya ada yang salah dengan ini, tapi itu menambah lebih banyak kata daripada sekadar pengenalan lokal.Meskipun saya tidak dapat mengutip penelitian khusus apa pun tentang masalah ini, saya akan menyarankan agar semua pemeran dalam kode Anda melanggar prinsip Don't Repeat Yourself. Apa yang tampaknya dilakukan oleh kode Anda adalah mengonversi
costIn
dancostOut
mengetikDecimal
, dan kemudian melakukan beberapa pemeriksaan kewarasan atas hasil konversi tersebut, dan melakukan operasi tambahan pada nilai-nilai yang dikonversi jika pemeriksaan lulus. Bahkan, kode Anda melakukan salah satu pemeriksaan kewarasan pada nilai yang belum dikonversi, meningkatkan kemungkinan bahwa costOut mungkin memiliki nilai yang lebih besar dari nol, tetapi kurang dari setengah ukuran dari bukan nol terkecil yangDecimal
dapat mewakili. Kode akan jauh lebih jelas jika mendefinisikan variabel tipeDecimal
untuk menyimpan nilai yang dikonversi, dan kemudian menindaklanjutinya.Tampaknya penasaran bahwa Anda akan lebih tertarik pada rasio
Decimal
representasicostIn
dancostOut
daripada rasio nilai aktualcostIn
dancostOut
, kecuali jika kode tersebut juga akan menggunakan representasi desimal untuk beberapa tujuan lain. Jika kode akan menggunakan representasi tersebut lebih lanjut, itu akan menjadi argumen lebih lanjut untuk membuat variabel untuk menahan representasi tersebut, daripada memiliki urutan gips yang berkelanjutan di seluruh kode.sumber
Decimal
dilemparkan oleh para pemain tergantung pada besarnya nilai yang dipermasalahkan, saya merasa sulit untuk membayangkan aturan bisnis yang akan memanggil cara para pemain sebenarnya akan berperilaku.Decimal
tipe tidak. Nilai 1.0d / 3.0 akan memiliki lebih banyak digit di sebelah kanan desimal daripada yang dapat dipertahankan saat menggunakan angka yang lebih besar. Jadi menambah dan mengurangi jumlah yang sama akan menyebabkan kehilangan presisi. Tipe titik tetap dapat kehilangan presisi dengan penggandaan atau pembagian fraksional, tetapi tidak dengan penambahan, pengurangan, perkalian, atau pembagian-dengan-sisa (misalnya 1,00 / 7 adalah 0,14 sisa 0,2; 1,00 div 0,15 adalah 6 sisa 0,10).Saya melihat kode itu dan bertanya "bagaimana biayanya 0 (atau kurang)?". Kasus khusus apa yang ditunjukkan? Kode seharusnya
Saya menduga nama di sini: ubah
BothCostsAreValidProducts
danNO_PROFIT
sepantasnya.sumber
if (CostIn <= 0 || CostOut <= 0)
baik-baik saja.Brevity berhenti menjadi suatu kebajikan ketika kita lupa bahwa itu berarti suatu tujuan daripada kebajikan itu sendiri. Kami menyukai keringkasan karena berkorelasi dengan kesederhanaan, dan kami menyukai kesederhanaan karena kode yang lebih sederhana lebih mudah dipahami dan lebih mudah untuk memodifikasi dan mengandung lebih sedikit bug. Pada akhirnya kami ingin kode untuk mencapai tujuan ini:
Memenuhi persyaratan bisnis dengan jumlah pekerjaan paling sedikit
Hindari bug
Izinkan kami melakukan perubahan di masa mendatang yang terus memenuhi 1 dan 2
Inilah tujuannya. Prinsip atau metode desain apa pun (baik itu CIUMAN, YAGNI, TDD, SOLID, bukti, sistem tipe, metaprogramming dinamis, dll.) Hanya berbudi luhur sejauh mereka membantu kami mencapai tujuan ini.
Garis yang dimaksud tampaknya telah meleset dari tujuan akhir. Meskipun pendek, itu tidak sederhana. Ini sebenarnya mengandung redundansi yang tidak perlu dengan mengulangi operasi casting yang sama beberapa kali. Kode berulang meningkatkan kompleksitas dan kemungkinan bug. Memadukan casting dengan perhitungan aktual juga membuat kode sulit untuk diikuti.
Jalur ini memiliki tiga masalah: Pelindung (casing khusus 0), tipe casting dan perhitungan. Setiap masalah cukup sederhana ketika diambil secara terpisah, tetapi karena semuanya telah berbaur dalam ekspresi yang sama, itu menjadi sulit untuk diikuti.
Tidak jelas mengapa
CostOut
tidak dilemparkan pertama kali digunakanCostIn
adalah. Mungkin ada alasan yang bagus, tetapi maksudnya tidak jelas (setidaknya bukan tanpa konteks) yang berarti pengelola akan berhati-hati mengubah kode ini karena mungkin ada beberapa asumsi tersembunyi. Dan ini adalah laknat untuk rawatan.Karena
CostIn
dilemparkan sebelum membandingkan dengan 0 Saya menganggap itu adalah nilai floating point. (Jika itu sebuah int tidak akan ada alasan untuk melemparkan). Tetapi jikaCostOut
float maka kode tersebut mungkin menyembunyikan bug divide-by-zero yang tidak jelas, karena nilai floating point mungkin kecil tetapi tidak nol, tetapi nol ketika dilemparkan ke desimal (setidaknya saya percaya ini akan mungkin terjadi.)Jadi masalahnya bukan singkatnya atau tidak adanya masalah, masalahnya adalah logika berulang dan perpaduan masalah yang mengarah ke kode yang sulit dipelihara.
Memperkenalkan variabel untuk menahan nilai yang dicor mungkin akan meningkatkan ukuran kode yang dihitung dalam jumlah token, tetapi akan mengurangi kompleksitas, memisahkan masalah, dan meningkatkan kejelasan, yang membawa kita lebih dekat ke tujuan kode yang lebih mudah dipahami dan dipelihara.
sumber
Keringkasan bukanlah suatu kebajikan sama sekali. Keterbacaan adalah kebajikan.
Brevity dapat menjadi alat dalam mencapai kebajikan, atau, seperti dalam contoh Anda, dapat menjadi alat dalam mencapai sesuatu yang berlawanan. Dengan cara ini atau yang lain, hampir tidak ada nilainya sendiri. Aturan bahwa kode harus "sesingkat mungkin" dapat diganti dengan "sesederhana mungkin" juga - mereka semua sama-sama tidak berarti dan berpotensi merusak jika mereka tidak melayani tujuan yang lebih besar.
Selain itu, kode yang Anda posting bahkan tidak mengikuti aturan singkatnya. Telah konstanta dideklarasikan dengan M akhiran, sebagian besar mengerikan
(decimal)
gips bisa dihindari, karena compiler akan mempromosikan tersisaint
untukdecimal
. Saya percaya bahwa orang yang Anda gambarkan hanya menggunakan singkat sebagai alasan. Kemungkinan besar tidak disengaja, tapi tetap saja.sumber
Dalam pengalaman bertahun-tahun, saya menjadi percaya bahwa singkatnya waktu adalah waktu - waktu mendominasi yang lainnya. Itu termasuk waktu kinerja - berapa lama program untuk melakukan pekerjaan - dan waktu pemeliharaan - berapa lama waktu untuk menambahkan fitur atau memperbaiki bug. (Bagaimana Anda menyeimbangkan keduanya tergantung pada seberapa sering kode tersebut dilakukan vs ditingkatkan - ingat bahwa pengoptimalan prematur masih merupakan akar dari semua kejahatan .) Keringkasan kode adalah untuk meningkatkan keringkasan keduanya; kode yang lebih pendek biasanya berjalan lebih cepat, dan biasanya lebih mudah dipahami dan karenanya dipelihara. Jika tidak melakukan keduanya, maka itu adalah net negative.
Dalam kasus yang ditunjukkan di sini, saya pikir singkatnya teks telah disalahartikan sebagai singkatnya jumlah baris, dengan mengorbankan keterbacaan, yang dapat meningkatkan waktu pemeliharaan. (Mungkin juga perlu waktu lebih lama untuk melakukan, tergantung pada bagaimana casting dilakukan, tetapi kecuali jika baris di atas dijalankan jutaan kali, itu mungkin bukan masalah.) Dalam hal ini, gips desimal berulang mengurangi keterbacaan, karena sulit untuk lihat apa perhitungan yang paling penting. Saya akan menulis sebagai berikut:
(Sunting: ini adalah kode yang sama dengan jawaban yang lain, jadi begitulah.)
Saya seorang penggemar operator ternary
? :
, jadi saya akan membiarkan itu masuksumber
? :
- Saya pikir contoh di atas cukup kompak, esp. dibandingkan dengan jika-maka-lain.:
.if-else
berbunyi seperti bahasa Inggris: jangan lewatkan artinya.Seperti hampir semua jawaban di atas keterbacaan harus selalu menjadi tujuan utama Anda. Namun saya juga berpikir memformat bisa menjadi cara yang lebih efektif untuk mencapai ini daripada menciptakan variabel dan baris baru.
Saya sangat setuju dengan argumen kompleksitas siklomatik dalam banyak kasus, namun fungsi Anda tampaknya kecil dan cukup sederhana untuk mengatasi dengan lebih baik dengan kasus uji yang baik. Karena penasaran mengapa harus dilemparkan ke desimal?
sumber
decimal
, bukan?double
! =decimal
, ada perbedaan besar.Bagi saya, sepertinya masalah besar dengan keterbacaan di sini terletak pada kurangnya format.
Saya akan menulis seperti ini:
Tergantung pada apakah tipe
CostIn
danCostOut
tipe floating-point atau tipe integral, beberapa gips mungkin juga tidak diperlukan. Berbeda denganfloat
dandouble
, nilai-nilai integral secara implisit dipromosikan kedecimal
.sumber
Kode dapat ditulis dengan tergesa-gesa, tetapi kode di atas harus di dunia saya ditulis dengan nama variabel yang jauh lebih baik.
Dan jika saya membaca kodenya dengan benar maka ia mencoba membuat perhitungan grossmargin.
sumber
Saya mengasumsikan CostIn * CostOut adalah bilangan bulat
Ini adalah bagaimana saya akan menuliskannya
M (Uang) adalah desimal
sumber
Kode ditulis untuk dipahami oleh orang-orang; singkatnya dalam hal ini tidak membeli banyak dan meningkatkan beban pada pengelola. Untuk singkatnya ini, Anda harus mengembangkannya dengan membuat kode lebih mendokumentasikan diri sendiri (nama variabel yang lebih baik) atau menambahkan lebih banyak komentar yang menjelaskan mengapa ia bekerja seperti ini.
Saat Anda menulis kode untuk memecahkan masalah hari ini, kode itu bisa menjadi masalah besok saat persyaratan berubah. Pemeliharaan selalu harus diperhitungkan dan meningkatkan pemahaman kode sangat penting.
sumber
Keringkasan tidak lagi menjadi kebajikan saat
sumber
Jika ini melewati tes unit validasi, maka itu akan baik-baik saja, jika spec baru ditambahkan, tes baru atau implementasi yang ditingkatkan diperlukan, dan itu diperlukan untuk "mengurai" kekasaran kode, saat itulah masalah akan muncul.
Jelas bug dalam kode menunjukkan bahwa ada masalah lain dengan Q / A yang merupakan kelalaian, sehingga fakta bahwa ada bug yang tidak tertangkap adalah memprihatinkan.
Ketika berhadapan dengan persyaratan non-fungsional seperti "keterbacaan" kode, kode tersebut harus didefinisikan oleh manajer pengembangan dan dikelola oleh pengembang utama dan dihormati oleh pengembang untuk memastikan implementasi yang tepat.
Cobalah untuk memastikan implementasi standar kode (standar dan konvensi) untuk memastikan "keterbacaan" dan kemudahan "pemeliharaan". Tetapi jika atribut kualitas ini tidak didefinisikan dan ditegakkan, maka Anda akan berakhir dengan kode seperti contoh di atas.
Jika Anda tidak suka melihat kode semacam ini, maka cobalah untuk mendapatkan tim dalam persetujuan tentang standar dan konvensi implementasi dan tuliskan dan lakukan tinjauan kode acak atau pemeriksaan spot untuk memvalidasi konvensi sedang dihormati.
sumber