Catatan Moderator
Pertanyaan ini sudah diposkan tujuh belas jawaban . Sebelum Anda memposting jawaban baru, harap baca jawaban yang ada dan pastikan sudut pandang Anda belum tercakup secara memadai.
Saya telah mengikuti beberapa praktik yang direkomendasikan dalam buku "Clean Code" Robert Martin, terutama yang berlaku untuk jenis perangkat lunak yang bekerja dengan saya dan yang masuk akal bagi saya (saya tidak mengikutinya sebagai dogma) .
Namun, satu efek samping yang saya perhatikan adalah kode "bersih" yang saya tulis, lebih banyak kode daripada jika saya tidak mengikuti beberapa praktik. Praktik spesifik yang mengarah ke ini adalah:
- Mengondisikan kondisional
Jadi, bukannya
if(contact.email != null && contact.emails.contains('@')
Saya bisa menulis metode kecil seperti ini
private Boolean isEmailValid(String email){...}
- Mengganti komentar sebaris dengan metode pribadi lain, sehingga nama metode menggambarkan dirinya sendiri daripada memiliki komentar sebaris di atasnya
- Kelas seharusnya hanya memiliki satu alasan untuk berubah
Dan beberapa lainnya. Intinya adalah, bahwa apa yang bisa menjadi metode 30 baris, akhirnya menjadi kelas, karena metode kecil yang menggantikan komentar dan merangkum persyaratan, dll. Ketika Anda menyadari bahwa Anda memiliki begitu banyak metode, maka "masuk akal" untuk letakkan semua fungsionalitas ke dalam satu kelas, padahal seharusnya itu adalah metode.
Saya sadar bahwa praktik apa pun yang dilakukan secara ekstrem dapat membahayakan.
Pertanyaan konkret yang saya cari jawabannya adalah:
Apakah ini produk sampingan yang dapat diterima dari penulisan kode bersih? Jika demikian, argumen apa yang dapat saya gunakan untuk membenarkan fakta bahwa lebih banyak LOC telah ditulis?
Organisasi ini tidak peduli secara khusus tentang lebih banyak LOC, tetapi lebih banyak LOC dapat menghasilkan kelas yang sangat besar (yang lagi-lagi, dapat diganti dengan metode yang panjang tanpa banyak fungsi pembantu sekali pakai demi kepentingan keterbacaan).
Ketika Anda melihat kelas yang cukup besar, itu memberi kesan bahwa kelas cukup sibuk, dan bahwa tanggung jawabnya telah selesai. Karenanya, Anda dapat membuat lebih banyak kelas untuk mencapai fungsionalitas lain. Hasilnya kemudian banyak kelas, semua melakukan "satu hal" dengan bantuan banyak metode pembantu kecil.
INI adalah perhatian khusus ... kelas-kelas itu bisa menjadi kelas tunggal yang masih mencapai "satu hal", tanpa bantuan banyak metode kecil. Itu bisa berupa kelas tunggal dengan mungkin 3 atau 4 metode dan beberapa komentar.
sumber
Jawaban:
Orang-orang ini telah mengidentifikasi sesuatu dengan benar: mereka ingin kode lebih mudah dipelihara. Di mana mereka salah meskipun mengasumsikan bahwa semakin sedikit kode yang ada, semakin mudah untuk mempertahankannya.
Agar kode mudah dipelihara, maka perlu diubah dengan mudah. Sejauh ini cara termudah untuk mencapai kode yang mudah diubah adalah dengan memiliki set lengkap tes otomatis untuk itu yang akan gagal jika perubahan Anda salah satu. Tes adalah kode, jadi menulis tes itu akan membengkak basis kode Anda. Dan itu adalah hal yang baik.
Kedua, untuk mengetahui apa yang perlu diubah, kode Anda harus mudah dibaca dan juga mudah dipikirkan. Kode yang sangat singkat, menyusut hanya untuk menjaga agar garis hitung mundur sangat tidak mudah dibaca. Jelas ada kompromi yang harus dipukul karena kode yang lebih panjang akan lebih lama untuk dibaca. Tetapi jika itu lebih cepat dipahami, maka itu sepadan. Jika tidak menawarkan manfaat itu, maka verbositas itu berhenti menjadi manfaat. Tetapi jika kode yang lebih lama meningkatkan keterbacaan maka sekali lagi ini adalah hal yang baik.
sumber
Ya, ini merupakan produk sampingan yang dapat diterima, dan pembenarannya adalah bahwa itu sekarang disusun sedemikian rupa sehingga Anda tidak harus membaca sebagian besar kode sebagian besar waktu. Alih-alih membaca fungsi 30-baris setiap kali Anda membuat perubahan, Anda membaca fungsi 5-baris untuk mendapatkan aliran keseluruhan, dan mungkin beberapa fungsi pembantu jika perubahan Anda menyentuh area itu. Jika kelas "ekstra" baru Anda dipanggil
EmailValidator
dan Anda tahu masalah Anda bukan dengan validasi email, Anda dapat melewatkan membacanya sama sekali.Ini juga lebih mudah untuk menggunakan kembali potongan yang lebih kecil, yang cenderung mengurangi jumlah baris Anda untuk keseluruhan program Anda. Sebuah
EmailValidator
dapat digunakan di semua tempat. Beberapa baris kode yang melakukan validasi email tetapi dikikis bersama dengan kode akses basis data tidak dapat digunakan kembali.Dan kemudian pertimbangkan apa yang perlu dilakukan jika aturan validasi email perlu diubah — yang Anda inginkan: satu lokasi yang diketahui; atau banyak lokasi, mungkin hilang beberapa?
sumber
iterations < _maxIterations
menjadi metode yang disebutShouldContinueToIterate
adalah bodoh .Bill Gates terkenal dikaitkan dengan mengatakan, "Mengukur kemajuan pemrograman dengan garis kode adalah seperti mengukur kemajuan pembangunan pesawat terbang berdasarkan berat."
Dengan rendah hati saya setuju dengan sentimen ini. Ini bukan untuk mengatakan bahwa suatu program harus berusaha untuk lebih atau kurang baris kode, tetapi ini bukanlah yang terpenting untuk menciptakan program yang berfungsi dan berfungsi. Ini membantu untuk mengingat bahwa pada akhirnya alasan di balik penambahan baris kode adalah karena secara teori lebih mudah dibaca.
Ketidaksepakatan dapat terjadi tentang apakah perubahan tertentu lebih atau kurang dapat dibaca, tetapi saya tidak berpikir Anda akan salah untuk membuat perubahan pada program Anda karena Anda berpikir dengan melakukan itu Anda membuatnya lebih mudah dibaca. Misalnya membuat sebuah
isEmailValid
bisa dianggap berlebihan dan tidak perlu, terutama jika itu disebut tepat sekali oleh kelas yang mendefinisikannya. Namun saya lebih suka melihatisEmailValid
dalam kondisi daripada serangkaian kondisi AND di mana saya harus menentukan apa kondisi masing-masing individu memeriksa dan mengapa itu sedang diperiksa.Di mana Anda mendapat masalah adalah ketika Anda membuat
isEmailValid
metode yang memiliki efek samping atau memeriksa hal-hal selain surel, karena ini lebih buruk daripada hanya menuliskan semuanya. Ini lebih buruk karena menyesatkan dan saya mungkin kehilangan bug karena itu.Meskipun jelas Anda tidak melakukan itu dalam kasus ini, jadi saya akan mendorong Anda untuk melanjutkan apa yang Anda lakukan. Anda harus selalu bertanya pada diri sendiri apakah dengan membuat perubahan, itu lebih mudah dibaca, dan jika itu adalah kasus Anda, maka lakukanlah!
sumber
Ini adalah masalah kehilangan pandangan pada tujuan yang sebenarnya.
Yang penting adalah menurunkan jam yang dihabiskan untuk pembangunan . Itu diukur dalam waktu (atau upaya yang setara), bukan dalam baris kode.
Ini seperti mengatakan bahwa pabrikan mobil harus membuat mobil mereka dengan sekrup yang lebih sedikit, karena dibutuhkan jumlah waktu yang tidak nol untuk memasukkan setiap sekrup. Walaupun itu benar benar, nilai pasar mobil tidak ditentukan oleh berapa banyak sekrup yang dibuatnya. atau tidak punya. Di atas segalanya, mobil harus performan, aman, dan mudah dirawat.
Sisa dari jawabannya adalah contoh bagaimana kode bersih dapat menyebabkan perolehan waktu.
Penebangan
Ambil aplikasi (A) yang tidak memiliki pendataan. Sekarang buat aplikasi B, yang merupakan aplikasi yang sama tetapi dengan logging. B akan selalu memiliki lebih banyak baris kode, dan dengan demikian Anda perlu menulis lebih banyak kode.
Tetapi banyak waktu akan menyelidiki masalah dan bug, dan mencari tahu apa yang salah.
Untuk aplikasi A, pengembang akan terjebak membaca kode, dan harus terus-menerus mereproduksi masalah dan melangkah melalui kode untuk menemukan sumber masalah. Ini berarti bahwa pengembang harus menguji dari awal eksekusi hingga akhir, di setiap lapisan yang digunakan, dan perlu mengamati setiap bagian logika yang digunakan.
Mungkin dia beruntung menemukannya segera, tetapi mungkin jawabannya ada di tempat terakhir yang dia pikirkan.
Untuk aplikasi B, dengan asumsi logging sempurna, pengembang mengamati log, dapat segera mengidentifikasi komponen yang salah, dan sekarang tahu ke mana harus mencari.
Ini bisa berupa hitungan menit, jam atau hari yang disimpan; tergantung pada ukuran dan kompleksitas basis kode.
Regresi
Ambil aplikasi A, yang tidak ramah sama sekali.
Ambil aplikasi B, yang KERING, tetapi akhirnya membutuhkan lebih banyak garis karena abstraksi tambahan.
Permintaan perubahan diajukan, yang membutuhkan perubahan logika.
Untuk aplikasi B, pengembang mengubah logika (unik, dibagi) sesuai dengan permintaan perubahan.
Untuk aplikasi A, pengembang harus mengubah semua instance dari logika ini di mana ia mengingatnya sedang digunakan.
Ini dapat menyebabkan pemborosan waktu yang sangat besar. Tidak hanya dalam pengembangan, tetapi dalam berburu dan menemukan bug. Aplikasi dapat mulai berperilaku tidak menentu dengan cara yang tidak mudah dipahami pengembang. Dan itu akan menyebabkan sesi debugging yang panjang.
Pertukaran pengembang
Pengembang Aplikasi yang dibuat A. Kode tidak bersih atau dapat dibaca, tetapi berfungsi seperti pesona dan telah berjalan dalam produksi. Tidak mengherankan, tidak ada dokumentasi juga.
Pengembang A tidak ada selama sebulan karena liburan. Permintaan perubahan darurat diajukan. Tidak bisa menunggu tiga minggu lagi untuk Dev A kembali.
Pengembang B harus melakukan perubahan ini. Dia sekarang perlu membaca seluruh basis kode, memahami bagaimana segala sesuatu bekerja, mengapa itu bekerja, dan apa yang ingin dicapai. Ini membutuhkan waktu lama, tetapi katakanlah dia bisa melakukannya dalam waktu tiga minggu.
Pada saat yang sama, aplikasi B (yang dev B buat) memiliki keadaan darurat. Dev B ditempati, tetapi Dev C tersedia, meskipun ia tidak tahu basis kode. Apa yang kita lakukan?
Dev A kembali dari liburannya, dan melihat bahwa B tidak memahami kode itu, dan karenanya menerapkannya dengan buruk. Itu bukan kesalahan B, karena ia menggunakan semua sumber daya yang tersedia, kode sumber tidak cukup dapat dibaca. Apakah A sekarang harus menghabiskan waktu memperbaiki pembacaan kode?
Semua masalah ini, dan banyak lagi, akhirnya membuang - buang waktu . Ya, dalam jangka pendek, kode bersih membutuhkan lebih banyak upaya sekarang , tetapi akan membayar dividen di kemudian hari ketika bug / perubahan yang tak terhindarkan perlu ditangani.
Manajemen perlu memahami bahwa tugas singkat sekarang akan menyelamatkan Anda beberapa tugas panjang di masa depan. Gagal merencanakan adalah rencana untuk gagal.
Penjelasan goto saya bertanya kepada manajemen apa yang mereka inginkan: aplikasi dengan basis kode 100KLOC yang dapat dikembangkan dalam tiga bulan, atau basis kode 50KLOC yang dapat dikembangkan dalam enam bulan.
Mereka jelas akan memilih waktu pengembangan yang lebih pendek, karena manajemen tidak peduli dengan KLOC . Manajer yang fokus pada KLOC mengelola mikro sementara tidak mendapat informasi tentang apa yang mereka coba kelola.
sumber
Saya pikir Anda harus sangat berhati-hati dalam menerapkan praktik "kode bersih" jika mereka mengarah pada kompleksitas yang lebih menyeluruh. Refactoring prematur adalah akar dari banyak hal buruk.
Mengekstrak kondisional ke fungsi mengarah ke kode yang lebih sederhana pada titik di mana kondisional diekstraksi , tetapi mengarah ke kompleksitas yang lebih menyeluruh karena Anda sekarang memiliki fungsi yang terlihat dari lebih banyak titik dalam program. Anda menambahkan sedikit kompleksitas ke semua fungsi lain di mana fungsi baru ini sekarang terlihat.
Saya tidak mengatakan Anda tidak harus mengekstrak kondisional, hanya saja Anda harus mempertimbangkan dengan hati-hati jika perlu.
Dalam semua hal di atas adalah alasan untuk ekstraksi di luar itu hanya menjadi "kode bersih". Selain itu, Anda mungkin tidak akan ragu apakah itu hal yang benar untuk dilakukan.
Saya akan mengatakan, jika ragu, selalu memilih kode yang paling sederhana dan paling mudah.
sumber
Saya akan menunjukkan tidak ada yang salah dengan ini:
Setidaknya dengan asumsi itu digunakan sekali ini.
Saya bisa mengalami masalah dengan ini dengan sangat mudah:
Beberapa hal yang saya perhatikan:
Jika sedang digunakan sekali, mudah diurai, dan membutuhkan kurang dari satu baris saya akan menebak keputusan kedua. Itu mungkin bukan sesuatu yang saya sebut jika bukan masalah khusus dari tim.
Di sisi lain saya telah melihat metode melakukan sesuatu seperti ini:
Contoh itu jelas bukan KERING.
Atau bahkan pernyataan terakhir itu bisa memberikan contoh lain:
Tujuannya adalah membuat kode lebih mudah dibaca:
Skenario lain:
Anda mungkin memiliki metode seperti:
Jika ini sesuai dengan logika bisnis Anda dan tidak digunakan kembali, tidak ada masalah di sini.
Tetapi ketika seseorang bertanya "Mengapa '@' diselamatkan, karena itu tidak benar!" dan Anda memutuskan untuk menambahkan validasi yang sebenarnya, lalu ekstrak!
Anda akan senang Anda lakukan ketika Anda juga perlu akun untuk akun email kedua presiden Pr3 $ sid3nt @ h0m3! @ Mydomain.com dan memutuskan untuk pergi keluar semua dan mencoba dan mendukung RFC 2822.
Keterbacaan:
Jika kode Anda jelas, Anda tidak perlu komentar di sini. Sebenarnya, Anda tidak perlu komentar untuk mengatakan apa yang paling sering dilakukan oleh kode, tetapi mengapa melakukannya:
Apakah komentar di atas pernyataan if atau di dalam metode kecil bagi saya, sangat bagus. Saya bahkan mungkin berpendapat sebaliknya berguna dengan komentar baik di dalam metode lain karena sekarang Anda harus menavigasi ke metode lain untuk melihat bagaimana dan mengapa ia melakukan apa yang dilakukannya.
Singkatnya: Jangan mengukur hal-hal ini; Berfokuslah pada prinsip-prinsip dari mana teks itu dibuat (KERING, SOLID, KISS).
sumber
Whether the comments above an if statement or inside a tiny method is to me, pedantic.
Ini adalah masalah "jerami yang mematahkan punggung unta". Anda benar bahwa hal yang satu ini tidak terlalu sulit untuk dibaca secara langsung. Tapi jika Anda memiliki metode besar (misalnya impor besar) yang memiliki puluhan ini evaluasi kecil, memiliki ini dikemas dalam nama metode yang dapat dibaca (IsUserActive
,GetAverageIncome
,MustBeDeleted
, ...) akan menjadi peningkatan yang nyata pada membaca kode. Masalah dengan contohnya adalah ia hanya mengamati satu jerami, bukan seluruh buntalan yang mematahkan punggung unta.if (contact.email != null && contact.email.contains('@'))
bermasalah. Jika if salah, tidak ada yang lain jika baris bisa benar. Ini sama sekali tidak terlihat diLooksSortaLikeAnEmail
blok. Fungsi yang berisi satu baris kode tidak jauh lebih baik daripada komentar yang menjelaskan cara kerja baris.Clean Code adalah buku yang sangat bagus, dan layak dibaca, tetapi bukan merupakan otoritas final dalam hal-hal tersebut.
Memecah kode menjadi fungsi-fungsi logis biasanya merupakan ide yang baik, tetapi beberapa programmer melakukannya sejauh Martin melakukannya - pada titik tertentu Anda mendapatkan hasil yang semakin berkurang dari mengubah segala sesuatu menjadi fungsi dan itu bisa sulit untuk diikuti ketika semua kode berada dalam ukuran kecil. potongan.
Salah satu opsi saat tidak layak membuat fungsi baru adalah dengan menggunakan variabel perantara:
Ini membantu menjaga kode mudah diikuti tanpa harus sering melompati file.
Masalah lain adalah bahwa Kode Bersih sudah cukup tua sebagai buku sekarang. Banyak rekayasa perangkat lunak telah bergerak ke arah pemrograman fungsional, sedangkan Martin berusaha keras untuk menambah keadaan ke berbagai hal dan membuat objek. Saya curiga dia akan menulis buku yang sangat berbeda jika dia menulisnya hari ini.
sumber
Mempertimbangkan fakta bahwa "adalah email yang valid" kondisi yang Anda miliki saat ini akan menerima alamat email yang sangat tidak valid "
@
", saya pikir Anda memiliki alasan untuk mencabut kelas EmailValidator. Lebih baik lagi, gunakan perpustakaan yang baik, teruji dengan baik untuk memvalidasi alamat email.Baris kode sebagai metrik tidak ada artinya. Pertanyaan penting dalam rekayasa perangkat lunak bukanlah:
Pertanyaan penting adalah:
Saya tidak pernah memikirkan LoC saat menulis kode untuk tujuan apa pun selain Code Golf. Saya bertanya pada diri sendiri, "Bisakah saya menulis ini dengan lebih ringkas?", Tetapi untuk tujuan keterbacaan, pemeliharaan, dan efisiensi, tidak hanya panjang.
Tentu, mungkin saya bisa menggunakan rantai panjang operasi boolean bukan metode utilitas, tetapi haruskah saya?
Pertanyaan Anda benar-benar membuat saya berpikir kembali pada rantai panjang boolean yang telah saya tulis dan sadari bahwa saya mungkin seharusnya menulis satu atau lebih metode utilitas.
sumber
Pada satu tingkat, mereka benar - kode kurang lebih baik. Jawaban lain yang dikutip Gate, saya lebih suka:
Singkatnya, semakin sedikit kode yang Anda miliki, semakin sedikit yang bisa salah. Jika sesuatu tidak perlu, potong saja.
Jika ada kode yang terlalu rumit, maka sederhanakan sampai semua elemen fungsional yang sebenarnya tetap ada.
Yang penting di sini, adalah bahwa semua ini merujuk pada fungsionalitas, dan hanya memiliki minimum yang diperlukan untuk melakukannya. Itu tidak mengatakan apa-apa tentang bagaimana itu diungkapkan.
Apa yang Anda lakukan dengan mencoba memiliki kode bersih tidak bertentangan dengan hal di atas. Anda menambahkan ke LOC Anda tetapi tidak menambahkan fungsionalitas yang tidak digunakan.
Tujuan akhirnya adalah memiliki kode yang dapat dibaca tetapi tidak ada tambahan yang berlebihan. Kedua prinsip tersebut seharusnya tidak saling bertentangan.
Metafora akan membangun mobil. Bagian fungsional dari kode adalah sasis, mesin, roda ... apa yang membuat mobil berjalan. Bagaimana Anda memecahnya lebih seperti suspensi, power steering dan sebagainya, itu membuatnya lebih mudah untuk ditangani. Anda ingin mekanik Anda sesederhana mungkin sambil tetap melakukan pekerjaan mereka, untuk meminimalkan kemungkinan terjadi kesalahan, tetapi itu tidak mencegah Anda dari memiliki kursi yang bagus.
sumber
Ada banyak kebijaksanaan dalam jawaban yang ada, tetapi saya ingin menambahkan satu faktor lagi: bahasa .
Beberapa bahasa mengambil lebih banyak kode daripada yang lain untuk mendapatkan efek yang sama. Secara khusus, sementara Java (yang saya duga adalah bahasa dalam pertanyaan) sangat terkenal dan umumnya sangat solid dan jelas dan langsung, beberapa bahasa yang lebih modern jauh lebih ringkas dan ekspresif.
Sebagai contoh, di Jawa dapat dengan mudah mengambil 50 baris untuk menulis kelas baru dengan tiga properti, masing-masing dengan pengambil dan penyetel, dan satu atau lebih konstruktor - sementara Anda dapat mencapai hal yang persis sama dalam satu baris Kotlin * atau Scala. (Tabungan Bahkan lebih besar jika Anda juga ingin cocok
equals()
,hashCode()
dantoString()
metode.)Hasilnya adalah bahwa di Jawa, pekerjaan tambahan berarti Anda lebih mungkin untuk menggunakan kembali objek umum yang tidak benar-benar cocok, untuk memeras properti menjadi objek yang ada, atau untuk melewati sekelompok properti 'telanjang' di sekitar secara individual; sementara dalam bahasa yang singkat dan ekspresif, Anda lebih cenderung menulis kode yang lebih baik.
(Ini menyoroti perbedaan antara kompleksitas 'permukaan' dari kode, dan kompleksitas dari ide / model / pemrosesan yang diterapkannya. Garis-garis kode bukanlah ukuran yang buruk dari yang pertama, tetapi lebih sedikit hubungannya dengan yang kedua .)
Jadi 'biaya' untuk melakukan sesuatu dengan benar tergantung pada bahasanya. Mungkin satu tanda bahasa yang baik adalah yang tidak membuat Anda memilih antara melakukan sesuatu dengan baik, dan melakukannya dengan mudah!
(* Ini bukan tempat yang tepat untuk plug, tapi Kotlin layak untuk dicoba.)
sumber
Mari kita asumsikan bahwa Anda sedang bekerja dengan kelas
Contact
saat ini. Fakta bahwa Anda menulis metode lain untuk validasi alamat email adalah bukti fakta bahwa kelasContact
tidak menangani satu tanggung jawab tunggal.Itu juga menangani beberapa tanggung jawab email, yang idealnya, harus kelas tersendiri.
Bukti lebih lanjut bahwa kode Anda adalah perpaduan
Contact
danEmail
kelas adalah bahwa Anda tidak akan dapat dengan mudah menguji kode validasi email. Dibutuhkan banyak manuver untuk mencapai kode validasi email dalam metode besar dengan nilai yang benar. Lihat metode yaitu di bawah ini.Di sisi lain, jika Anda memiliki kelas Email terpisah dengan metode untuk validasi email, maka untuk menguji unit kode validasi Anda, Anda hanya akan membuat satu panggilan sederhana
Email.Validation()
dengan data pengujian Anda.Konten Bonus: Pembicaraan MFeather tentang sinergi yang mendalam antara testabilitas dan desain yang baik.
sumber
Pengurangan LOC telah ditemukan berkorelasi dengan berkurangnya cacat, tidak ada yang lain. Dengan asumsi kemudian bahwa setiap kali Anda mengurangi LOC, Anda telah mengurangi kemungkinan cacat pada dasarnya jatuh ke dalam perangkap percaya bahwa korelasi sama dengan sebab-akibat. Pengurangan LOC adalah hasil dari praktik pengembangan yang baik dan bukan apa yang membuat kode baik.
Dalam pengalaman saya, orang yang dapat memecahkan masalah dengan kode lebih sedikit (pada level makro) cenderung lebih terampil daripada mereka yang menulis lebih banyak kode untuk melakukan hal yang sama. Apa yang dilakukan oleh pengembang yang terampil ini untuk mengurangi baris kode adalah menggunakan / membuat abstraksi dan solusi yang dapat digunakan kembali untuk memecahkan masalah umum. Mereka tidak menghabiskan waktu menghitung baris kode dan merasa tersiksa apakah mereka dapat memotong garis di sini atau di sana. Seringkali kode yang mereka tulis lebih bertele-tele daripada yang diperlukan, mereka hanya menulis lebih sedikit.
Biarkan saya memberi Anda sebuah contoh. Saya harus berurusan dengan logika di sekitar periode waktu dan bagaimana mereka tumpang tindih, apakah mereka berdekatan, dan kesenjangan apa yang ada di antara mereka. Ketika saya pertama kali mulai mengerjakan masalah ini, saya akan memiliki blok kode melakukan perhitungan di mana-mana. Akhirnya, saya membangun kelas untuk mewakili periode waktu dan operasi yang menghitung tumpang tindih, komplemen, dll. Ini segera menghapus sejumlah besar kode dan mengubahnya menjadi beberapa metode panggilan. Tetapi kelas-kelas itu sendiri tidak ditulis dengan singkat sama sekali.
Secara jelas menyatakan: jika Anda mencoba mengurangi LOC dengan mencoba memotong garis kode di sini atau di sana dengan lebih singkat, Anda salah melakukannya. Ini seperti mencoba menurunkan berat badan dengan mengurangi jumlah sayuran yang Anda makan. Tulis kode yang mudah dimengerti, dipelihara, dan didebug dan kurangi LOC melalui penggunaan kembali dan abstraksi.
sumber
Anda telah mengidentifikasi trade-off yang valid
Jadi memang ada trade-off di sini dan itu melekat pada abstraksi secara keseluruhan. Setiap kali ada orang yang mencoba menarik N baris kode ke dalam fungsinya sendiri untuk memberi nama dan mengisolasinya, mereka secara bersamaan membuat membaca situs panggilan lebih mudah (dengan merujuk pada nama daripada semua detail berdarah yang mendasari nama itu) dan lebih kompleks (Anda sekarang memiliki makna yang terjerat di dua bagian berbeda dari basis kode). "Mudah" adalah kebalikan dari "keras," tetapi itu bukan sinonim untuk "sederhana" yang merupakan kebalikan dari "kompleks." Keduanya tidak bertolak belakang, dan abstraksi selalu meningkatkan kompleksitas untuk memasukkan beberapa bentuk dengan mudah.
Kita dapat melihat kompleksitas yang ditambahkan secara langsung ketika beberapa perubahan dalam persyaratan bisnis membuat abstraksi mulai bocor. Mungkin beberapa logika baru akan berjalan paling alami di tengah kode pra-abstrak, katakan misalnya jika kode abstrak melintasi beberapa pohon dan Anda benar-benar ingin mengumpulkan (dan mungkin bertindak berdasarkan) semacam informasi saat Anda berada melintasi pohon itu. Sementara itu, jika Anda telah mengabstraksikan kode ini, maka mungkin ada situs panggilan lain, dan menambahkan logika yang diperlukan ke tengah metode ini dapat merusak situs panggilan lain tersebut. Lihat, setiap kali kita mengubah satu baris kode, kita hanya perlu melihat konteks langsung dari baris kode itu; ketika kita mengubah metode, kita harus Cmd-F seluruh kode sumber kita mencari apa pun yang mungkin rusak sebagai akibat dari mengubah kontrak metode itu,
Algoritma serakah bisa gagal dalam kasus ini
Kompleksitasnya juga membuat kode dalam arti tertentu lebih mudah dibaca daripada lebih banyak. Dalam pekerjaan sebelumnya saya berurusan dengan API HTTP yang sangat hati-hati dan tepat terstruktur menjadi beberapa lapisan, setiap titik akhir ditentukan oleh pengontrol yang memvalidasi bentuk pesan yang masuk dan kemudian menyerahkannya ke manajer "business-logic-layer" , yang kemudian membuat beberapa permintaan "lapisan data" yang bertanggung jawab untuk membuat beberapa pertanyaan ke lapisan "objek akses data", yang bertanggung jawab untuk membuat beberapa Delegasi SQL yang sebenarnya akan menjawab pertanyaan Anda. Hal pertama yang bisa saya katakan tentang hal itu adalah, sekitar 90% dari kode itu adalah copy-and-paste boilerplate, dengan kata lain itu adalah no-ops. Jadi dalam banyak kasus membaca setiap bagian kode yang diberikan sangat "mudah", karena "oh manajer ini hanya meneruskan permintaan ke objek akses data itu."banyak pengalihan konteks dan menemukan file dan mencoba melacak informasi yang seharusnya tidak pernah Anda lacak, "ini disebut X pada layer ini, ia disebut X 'pada layer lain ini, kemudian disebut X' 'pada layer lain ini lapisan lain. "
Saya pikir ketika saya tinggalkan, CRUD API sederhana ini berada pada tahap di mana jika Anda mencetaknya pada 30 baris per halaman, itu akan memakan 10-20 lima ratus halaman buku teks di rak: itu adalah seluruh ensiklopedia repetitif kode. Dalam hal kompleksitas esensial, saya tidak yakin bahwa ada bahkan setengah dari buku teks kompleksitas esensial di sana; kami hanya memiliki 5-6 diagram database untuk mengatasinya. Membuat sedikit perubahan pada itu adalah usaha yang sangat besar, belajar itu adalah usaha yang sangat besar, menambahkan fungsionalitas baru menjadi sangat menyakitkan sehingga kami benar-benar memiliki file templat boilerplate yang akan kami gunakan untuk menambahkan fungsionalitas baru.
Jadi saya telah melihat sendiri bagaimana membuat setiap bagian sangat mudah dibaca dan jelas dapat membuat keseluruhannya sangat tidak dapat dibaca dan tidak jelas. Ini berarti bahwa algoritma serakah dapat gagal. Anda tahu algoritma serakah, ya? "Saya akan melakukan langkah apa pun yang paling meningkatkan situasi lokal, dan kemudian saya akan percaya bahwa saya menemukan diri saya dalam situasi yang meningkat secara global." Ini sering merupakan upaya pertama yang indah tetapi juga bisa gagal dalam konteks yang kompleks. Sebagai contoh di bidang manufaktur, Anda mungkin mencoba meningkatkan efisiensi setiap langkah tertentu dalam proses pembuatan yang kompleks - lakukan batch yang lebih besar, berteriak pada orang-orang di lantai yang tampaknya tidak melakukan apa pun untuk membuat tangan mereka sibuk dengan hal lain - dan ini seringkali dapat menghancurkan efisiensi global sistem.
Praktik terbaik: gunakan KERING dan panjang untuk melakukan panggilan
(Catatan: Judul bagian ini agak bercanda; Saya sering memberi tahu teman-teman saya bahwa ketika seseorang mengatakan "kita harus melakukan X karena praktik terbaik mengatakan demikian " mereka 90% dari waktu tidak berbicara tentang sesuatu seperti injeksi SQL atau kata sandi hashing atau apa pun - praktik terbaik unilateral - dan dengan demikian pernyataan tersebut dapat diterjemahkan dalam 90% waktu untuk "kita harus melakukan X karena saya mengatakan demikian ." Seperti mereka mungkin memiliki beberapa artikel blog dari beberapa bisnis yang melakukan pekerjaan yang lebih baik dengan X daripada X 'tetapi umumnya tidak ada jaminan bahwa bisnis Anda menyerupai bisnis itu, dan umumnya ada beberapa artikel lain dari beberapa bisnis lain yang melakukan pekerjaan yang lebih baik dengan X' daripada X. Jadi tolong jangan mengambil judul juga serius.)
Apa yang saya sarankan didasarkan pada ceramah oleh Jack Diederich yang disebut Stop Writing Classes (youtube.com) . Dia membuat beberapa poin bagus dalam pembicaraan itu: misalnya, bahwa Anda dapat mengetahui kelas benar-benar hanya berfungsi ketika hanya memiliki dua metode publik, dan salah satunya adalah konstruktor / initializer. Tetapi dalam satu kasus dia berbicara tentang bagaimana perpustakaan hipotetis yang dia ganti string untuk pembicaraan sebagai "Muffin" menyatakan kelasnya sendiri "MuffinHash" yang merupakan subkelas dari
dict
tipe builtin yang dimiliki oleh Python. Implementasinya benar-benar kosong — seseorang baru saja berpikir, "kita mungkin perlu menambahkan fungsionalitas khusus ke kamus Python nanti, mari kita perkenalkan abstraksi sekarang untuk berjaga-jaga."Dan jawabannya adalah, "kita selalu bisa melakukannya nanti, jika perlu."
Saya pikir kita kadang-kadang berpura-pura seperti kita akan menjadi programmer yang lebih buruk di masa depan daripada kita sekarang, jadi kita mungkin ingin memasukkan semacam hal kecil yang mungkin membuat kita bahagia di masa depan. Kami mengantisipasi kebutuhan masa depan-kita. "Jika lalu lintas 100 kali lebih besar dari yang kami kira, pendekatan itu tidak akan berkembang, jadi kami harus menempatkan investasi di muka ke dalam pendekatan yang lebih sulit ini yang akan ditingkatkan." Sangat mencurigakan.
Jika kita menanggapi saran itu dengan serius maka kita perlu mengidentifikasi kapan "nanti" telah datang. Mungkin hal yang paling jelas adalah menetapkan batas atas panjangnya barang untuk alasan gaya. Dan saya pikir saran terbaik yang tersisa adalah menggunakan KERING — jangan ulangi diri Anda sendiri — dengan heuristik ini tentang panjang garis untuk menambal lubang dalam prinsip-prinsip SOLID. Berdasarkan heuristik 30 baris menjadi "halaman" teks dan analogi dengan prosa,
Seperti yang saya sebutkan di sana, saya menguji statistik ini terhadap pohon sumber Linux saat ini untuk menemukan perkiraan persentase itu, tetapi mereka juga semacam alasan dalam analogi sastra.
sumber