Konteks
Saya baru-baru ini tertarik untuk menghasilkan kode yang diformat lebih baik. Dan yang lebih baik saya maksudkan "mengikuti aturan yang didukung oleh cukup banyak orang untuk menganggapnya sebagai praktik yang baik" (karena tentu saja tidak akan ada satu cara "terbaik" yang unik untuk kode, tentu saja).
Hari-hari ini, aku kebanyakan kode Ruby jadi aku mulai menggunakan linter (Rubocop) untuk memberikan saya beberapa info tentang "kualitas" dari kode saya (ini "kualitas" yang didefinisikan oleh proyek berbasis masyarakat ruby-style-panduan ).
Perhatikan bahwa saya akan menggunakan "kualitas" seperti dalam "kualitas Formating", tidak begitu banyak tentang efisiensi kode, bahkan jika dalam beberapa kasus, efisiensi kode sebenarnya adalah dipengaruhi oleh bagaimana kode ditulis.
Bagaimanapun, melakukan semua itu, saya menyadari (atau setidaknya, ingat) beberapa hal:
- Beberapa bahasa (terutama Python, Ruby, dan semacamnya) memungkinkan untuk membuat satu baris kode yang hebat
- Mengikuti beberapa pedoman untuk kode Anda dapat membuatnya secara signifikan lebih pendek dan masih sangat jelas
- Namun, mengikuti pedoman ini terlalu ketat dapat membuat kode menjadi kurang jelas / mudah dibaca
- Kode dapat menghormati beberapa pedoman hampir dengan sempurna dan masih berkualitas buruk
- Keterbacaan kode sebagian besar subyektif (seperti dalam "apa yang saya temukan jelas benar-benar tidak jelas bagi sesama pengembang")
Itu hanya pengamatan, bukan aturan mutlak tentu saja. Anda juga akan mencatat bahwa keterbacaan kode dan pedoman berikut mungkin tampak tidak berhubungan pada titik ini, tetapi di sini pedoman adalah cara untuk mempersempit jumlah cara untuk menulis ulang satu potong kode.
Sekarang, beberapa contoh, untuk membuatnya lebih jelas.
Contohnya
Mari kita ambil contoh penggunaan sederhana: kami memiliki aplikasi dengan User
model " ". Seorang pengguna memiliki opsional firstname
dan surname
dan wajibemail
.
Saya ingin menulis metode " name
" yang akan mengembalikan lalu memberi nama ( firstname + surname
) pada pengguna jika setidaknya ada firstname
atau surname
tidak, atauemail
sebagai nilai cadangan jika tidak.
Saya juga ingin metode ini mengambil " use_email
" sebagai parameter (boolean), memungkinkan untuk menggunakan email pengguna sebagai nilai fallback. use_email
Parameter " " ini seharusnya default (jika tidak dilewatkan) sebagai " true
".
Cara paling sederhana untuk menulis itu, di Ruby, adalah:
def name(use_email = true)
# If firstname and surname are both blank (empty string or undefined)
# and we can use the email...
if (firstname.blank? && surname.blank?) && use_email
# ... then, return the email
return email
else
# ... else, concatenate the firstname and surname...
name = "#{firstname} #{surname}"
# ... and return the result striped from leading and trailing spaces
return name.strip
end
end
Kode ini adalah cara yang paling sederhana dan mudah dipahami untuk melakukannya. Bahkan untuk seseorang yang tidak "berbicara" Ruby.
Sekarang mari kita coba untuk mempersingkat:
def name(use_email = true)
# 'if' condition is used as a guard clause instead of a conditional block
return email if (firstname.blank? && surname.blank?) && use_email
# Use of 'return' makes 'else' useless anyway
name = "#{firstname} #{surname}"
return name.strip
end
Ini lebih pendek, masih mudah dipahami, jika tidak mudah (klausa penjaga lebih alami untuk dibaca daripada blok bersyarat). Penjaga klausa juga membuatnya lebih sesuai dengan pedoman yang saya gunakan, jadi menang-menang di sini. Kami juga mengurangi level indentasi.
Sekarang mari kita gunakan beberapa sihir Ruby untuk membuatnya lebih pendek:
def name(use_email = true)
return email if (firstname.blank? && surname.blank?) && use_email
# Ruby can return the last called value, making 'return' useless
# and we can apply strip directly to our string, no need to store it
"#{firstname} #{surname}".strip
end
Bahkan lebih pendek dan mengikuti pedoman dengan sempurna ... tetapi jauh lebih tidak jelas karena kurangnya pernyataan pengembalian membuatnya agak membingungkan bagi mereka yang tidak terbiasa dengan praktik ini.
Di sinilah kita dapat mulai mengajukan pertanyaan: apakah itu benar-benar layak? Jika kita mengatakan "tidak, buat itu bisa dibaca dan tambahkan ' return
'" (mengetahui ini tidak akan mematuhi pedoman). Atau haruskah kita berkata, "Tidak apa-apa, itu cara Ruby, belajar bahasa sialan!"?
Jika kita mengambil opsi B, mengapa tidak membuatnya lebih pendek:
def name(use_email = true)
(email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end
Ini dia, satu-liner! Tentu saja lebih pendek ... di sini kita mengambil keuntungan dari kenyataan bahwa Ruby akan mengembalikan nilai atau yang lain tergantung mana yang didefinisikan (karena email akan didefinisikan dalam kondisi yang sama seperti sebelumnya).
Kita juga bisa menulisnya:
def name(use_email = true)
(email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end
Ini singkat, tidak terlalu sulit untuk dibaca (maksud saya, kita semua telah melihat seperti apa bentuk one-liner yang jelek), Ruby yang bagus, itu sesuai dengan pedoman yang saya gunakan ... Tapi tetap saja, dibandingkan dengan cara pertama menulis itu, itu jauh lebih mudah dibaca dan dimengerti. Kami juga dapat berpendapat bahwa baris ini terlalu panjang (lebih dari 80 karakter).
Pertanyaan
Beberapa contoh kode dapat menunjukkan bahwa memilih antara kode "ukuran penuh" dan banyak versi yang diperkecil (turun ke garis satu yang terkenal) bisa jadi sulit karena, seperti yang bisa kita lihat, satu-baris bisa tidak menakutkan tetapi tetap saja, tidak ada yang akan mengalahkan kode "ukuran penuh" dalam hal keterbacaan ...
Jadi, inilah pertanyaan sebenarnya: ke mana harus berhenti? Kapan pendek, cukup pendek? Bagaimana cara mengetahui kapan kode menjadi "terlalu pendek" dan kurang dapat dibaca (dengan mengingat bahwa itu cukup subjektif)? Dan lebih dari itu: bagaimana cara selalu kode sesuai dan menghindari pencampuran satu-liners dengan potongan "ukuran penuh" kode ketika saya hanya merasa seperti itu?
TL; DR
Pertanyaan utama di sini adalah: ketika datang untuk memilih antara "sepotong kode yang panjang tapi jelas, dapat dibaca dan dimengerti" dan "kuat, lebih pendek namun lebih sulit untuk dibaca / dipahami satu-liner", mengetahui keduanya adalah yang teratas dan yang bawah skala dan bukan hanya dua opsi: bagaimana mendefinisikan di mana perbatasan antara "cukup jelas" dan "tidak sejelas yang seharusnya"?
Pertanyaan utama bukanlah klasik "One-liners vs keterbacaan: mana yang lebih baik?" tetapi "Bagaimana menemukan keseimbangan di antara keduanya?"
Edit 1
Komentar dalam contoh kode dimaksudkan untuk "diabaikan", mereka di sini untuk mengklarifikasi apa yang terjadi, tetapi tidak diperhitungkan ketika mengevaluasi keterbacaan kode.
return
kata kunci yang ditambahkan . Ketujuh karakter itu menambah kejernihan di mataku.
[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip
... karena false.blank?
mengembalikan true dan operator ternary menghemat beberapa karakter ... ¯ \ _ (ツ) _ / ¯
return
seharusnya ditambahkan kata kunci ?! Tidak memberikan informasi apa pun . Ini kekacauan murni.