Benar atau salah, saya saat ini berkeyakinan bahwa saya harus selalu berusaha membuat kode saya sekuat mungkin, bahkan jika ini berarti menambahkan kode berlebihan / memeriksa yang saya tahu tidak akan ada gunanya saat ini, tetapi mereka mungkin x jumlah tahun ke depan.
Misalnya, saya sedang mengerjakan aplikasi seluler yang memiliki kode ini:
public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)
{
//1. Is rows equal to null? - This will be the case if this is the first appointment.
if (rows == null) {
rows = new List<CalendarRow> ();
}
//2. Is rows empty? - This will be the case if this is the first appointment / some other unknown reason.
if(rows.Count == 0)
{
rows.Add (new CalendarRow (0));
rows [0].Appointments.Add (app);
}
//blah...
}
Melihat secara khusus pada bagian dua, saya tahu bahwa jika bagian satu benar, maka bagian dua juga akan benar. Saya tidak dapat memikirkan alasan mengapa bagian satu akan salah dan bagian dua mengevaluasi benar, yang membuat if
pernyataan kedua berlebihan.
Namun, mungkin ada kasus di masa depan di mana if
pernyataan kedua ini sebenarnya dibutuhkan, dan untuk alasan yang diketahui.
Beberapa orang mungkin melihat ini pada awalnya dan berpikir saya pemrograman dengan masa depan dalam pikiran, yang jelas merupakan hal yang baik. Tapi saya tahu beberapa contoh di mana kode semacam ini memiliki bug "tersembunyi" dari saya. Berarti saya perlu waktu lebih lama untuk mencari tahu mengapa fungsi xyz
dilakukan abc
padahal seharusnya itu dilakukan def
.
Di sisi lain, ada juga banyak contoh di mana kode semacam ini telah membuatnya jauh, lebih mudah untuk meningkatkan kode dengan perilaku baru, karena saya tidak harus kembali dan memastikan bahwa semua cek yang relevan sudah ada.
Apakah ada umum aturan-of-thumb pedoman untuk jenis kode? (Saya juga tertarik mendengar apakah ini akan dianggap praktik yang baik atau buruk?)
NB: Ini bisa dianggap mirip dengan pertanyaan ini, namun tidak seperti pertanyaan itu, saya ingin jawaban dengan asumsi tidak ada tenggat waktu.
TLDR: Haruskah saya sejauh menambahkan kode berlebihan untuk membuatnya berpotensi lebih kuat di masa depan?
if(rows.Count == 0)
tidak akan pernah terjadi, maka Anda bisa mengajukan pengecualian ketika itu terjadi - dan periksa mengapa asumsi Anda menjadi salah.rows
bisa nol? Tidak pernah ada alasan yang bagus, setidaknya di .NET, untuk koleksi menjadi nol. Kosong , tentu, tetapi tidak nol . Saya akan melempar pengecualian jikarows
nol, karena itu berarti ada kesalahan dalam logika oleh pemanggil.Jawaban:
Sebagai latihan, pertama-tama mari kita verifikasi logika Anda. Meskipun seperti yang akan kita lihat, Anda memiliki masalah lebih besar daripada masalah logis apa pun.
Sebut kondisi pertama A dan kondisi kedua B.
Anda pertama kali mengatakan:
Yaitu: A menyiratkan B, atau, dalam istilah yang lebih mendasar
(NOT A) OR B
Lalu:
Yaitu:
NOT((NOT A) AND B)
. Terapkan hukum Demorgan untuk mendapatkan(NOT B) OR A
B yang menyiratkan A.Karena itu, jika kedua pernyataan Anda benar maka A menyiratkan B dan B menyiratkan A, yang berarti keduanya harus sama.
Karena itu ya, ceknya berlebihan. Anda tampaknya memiliki empat jalur kode melalui program ini tetapi pada kenyataannya Anda hanya memiliki dua jalur.
Jadi sekarang pertanyaannya adalah: bagaimana cara menulis kode? Pertanyaan sebenarnya adalah: apa kontrak metode yang dinyatakan ? Pertanyaan apakah kondisinya berlebihan adalah ikan haring merah. Pertanyaan sebenarnya adalah "sudahkah saya merancang kontrak yang masuk akal, dan apakah metode saya jelas menerapkan kontrak itu?"
Mari kita lihat deklarasi:
Ini publik, jadi harus kuat terhadap data buruk dari penelepon yang sewenang-wenang.
Ini mengembalikan nilai, jadi itu harus berguna untuk nilai pengembaliannya, bukan untuk efek sampingnya.
Namun nama metode ini adalah kata kerja, menunjukkan bahwa itu berguna untuk efek sampingnya.
Kontrak dari parameter daftar adalah:
Kontrak ini gila . Bayangkan menulis dokumentasi untuk ini! Bayangkan menulis test case!
Saran saya: mulai dari awal. API ini memiliki antarmuka mesin permen yang ditulis di atasnya. (Ungkapan ini berasal dari cerita lama tentang mesin permen di Microsoft, di mana harga dan pilihannya adalah angka dua digit, dan sangat mudah untuk mengetik "85" yang merupakan harga item 75, dan Anda mendapatkan item yang salah. Fakta menyenangkan: ya, saya benar-benar melakukannya secara tidak sengaja ketika saya mencoba mengeluarkan permen karet dari mesin penjual otomatis di Microsoft!)
Inilah cara merancang kontrak yang masuk akal:
Jadikan metode Anda bermanfaat untuk efek samping atau nilai kembalinya, bukan keduanya.
Jangan terima jenis yang bisa berubah sebagai input, seperti daftar; jika Anda memerlukan urutan informasi, ambil nomor IEnumerable. Hanya baca urutannya; jangan menulis ke koleksi yang lewat kecuali sangat jelas bahwa ini adalah kontrak dari metode ini. Dengan mengambil IEnumerable Anda mengirim pesan ke pemanggil bahwa Anda tidak akan mengubah koleksi mereka.
Jangan pernah menerima nol; urutan nol adalah kekejian. Mengharuskan pemanggil untuk melewati urutan kosong jika itu masuk akal, tidak pernah nol.
Hancurkan segera jika penelepon melanggar kontrak Anda, untuk mengajari mereka bahwa Anda serius, dan agar mereka menangkap bug dalam pengujian, bukan produksi.
Rancang kontrak terlebih dahulu agar masuk akal mungkin, dan kemudian jelas menerapkan kontrak. Itulah cara untuk membuktikan desain masa depan.
Sekarang, saya hanya berbicara tentang kasus spesifik Anda, dan Anda mengajukan pertanyaan umum. Jadi, inilah beberapa saran umum tambahan:
Jika ada fakta bahwa Anda sebagai pengembang dapat menyimpulkan tetapi kompiler tidak bisa, maka gunakan pernyataan untuk mendokumentasikan fakta itu. Jika pengembang lain, seperti Anda di masa depan atau salah satu rekan kerja Anda, melanggar asumsi itu maka pernyataan itu akan memberi tahu Anda.
Dapatkan alat cakupan tes. Pastikan tes Anda mencakup setiap baris kode. Jika ada kode yang tidak terbuka maka Anda memiliki tes yang hilang, atau Anda memiliki kode mati. Kode mati secara mengejutkan berbahaya karena biasanya tidak dimaksudkan untuk mati! Kelemahan keamanan "kegagalan goto" Apple yang mengerikan beberapa tahun yang lalu segera muncul di benak.
Dapatkan alat analisis statis. Heck, dapatkan beberapa; setiap alat memiliki kekhususannya sendiri dan tidak ada yang superset dari yang lain. Perhatikan ketika memberitahu Anda bahwa ada kode yang tidak dapat dijangkau atau berlebihan. Sekali lagi, itu kemungkinan bug.
Jika kedengarannya seperti yang saya katakan: pertama, desain kodenya dengan baik, dan kedua, ujilah itu untuk memastikan sudah benar hari ini, well, itulah yang saya katakan. Melakukan hal-hal itu akan membuat berurusan dengan masa depan jauh lebih mudah; bagian tersulit tentang masa depan adalah berurusan dengan semua kode mesin permen kereta yang ditulis orang di masa lalu. Lakukan dengan benar hari ini dan biayanya akan lebih rendah di masa depan.
sumber
CompareExchange
tanpa kemampuan seperti itu!), Dan bahkan dalam skenario non-konkuren, sesuatu seperti "tambahkan catatan jika tidak ada, dan kembalikan baik yang sudah lewat catatan atau yang ada "bisa lebih nyaman daripada pendekatan apa pun yang tidak menggunakan efek samping dan nilai pengembalian.Apa yang Anda lakukan dalam kode yang Anda tunjukkan di atas tidak begitu banyak pemeriksaan di masa depan seperti halnya coding defensif .
Kedua
if
pernyataan menguji untuk hal-hal yang berbeda. Keduanya adalah tes yang tepat tergantung pada kebutuhan Anda.Bagian 1 menguji dan mengoreksi
null
objek. Catatan samping: Membuat daftar tidak membuat item anak (misalnya,CalendarRow
).Bagian 2 menguji dan memperbaiki kesalahan pengguna dan / atau implementasi. Hanya karena Anda memiliki
List<CalendarRow>
, bukan berarti Anda memiliki item dalam daftar. Pengguna dan pelaksana akan melakukan hal-hal yang tidak dapat Anda bayangkan hanya karena mereka diizinkan melakukannya, apakah itu masuk akal bagi Anda atau tidak.sumber
Saya kira, pertanyaan ini pada dasarnya tidak sesuai selera. Ya, itu ide yang baik untuk menulis kode yang kuat, namun kode dalam contoh Anda adalah sedikit pelanggaran terhadap prinsip KISS (karena banyak kode "bukti masa depan" akan seperti itu).
Saya pribadi kemungkinan tidak akan repot membuat kode anti peluru untuk masa depan. Saya tidak tahu masa depan, dan karena itu, kode "masa depan-bukti" seperti itu pasti gagal total ketika masa depan tiba.
Sebagai gantinya, saya lebih suka pendekatan yang berbeda: Buatlah asumsi yang Anda buat secara eksplisit menggunakan fasilitas
assert()
makro atau serupa. Dengan begitu, ketika masa depan datang, ia akan memberi tahu Anda dengan tepat di mana asumsi Anda tidak berlaku lagi.sumber
Prinsip lain yang mungkin ingin Anda pikirkan adalah ide gagal cepat . Idenya adalah ketika ada sesuatu yang salah dalam program Anda, Anda ingin segera menghentikan program tersebut, setidaknya saat Anda sedang mengembangkannya sebelum merilisnya. Di bawah prinsipal ini, Anda ingin menulis banyak cek untuk memastikan asumsi Anda berlaku, tetapi pertimbangkan dengan serius agar program Anda berhenti berjalan di jalurnya setiap kali asumsi dilanggar.
Singkatnya, jika ada kesalahan kecil di program Anda, Anda ingin crash sepenuhnya saat Anda menonton!
Ini mungkin terdengar berlawanan dengan intuisi, tetapi memungkinkan Anda menemukan bug secepat mungkin selama pengembangan rutin. Jika Anda menulis sepotong kode, dan Anda pikir sudah selesai, tetapi crash ketika Anda mengujinya, tidak ada pertanyaan bahwa Anda belum selesai. Selain itu, sebagian besar bahasa pemrograman memberi Anda alat debugging luar biasa yang paling mudah digunakan ketika program Anda benar-benar macet daripada mencoba melakukan yang terbaik setelah kesalahan. Contoh terbesar dan paling umum adalah bahwa jika Anda menghentikan program dengan melemparkan pengecualian yang tidak ditangani, pesan pengecualian akan memberi tahu Anda sejumlah besar informasi tentang bug, termasuk baris kode mana yang gagal dan jalur melalui kode Anda yang diambil oleh program jalannya ke baris kode (jejak stack).
Untuk pemikiran lebih lanjut, baca esai singkat ini: Jangan Memaku Program Anda di Posisi Tegak
Ini relevan bagi Anda karena ada kemungkinan bahwa kadang-kadang, cek yang Anda tulis ada di sana karena Anda ingin program Anda mencoba untuk tetap berjalan bahkan setelah ada kesalahan. Sebagai contoh, pertimbangkan implementasi singkat dari deret Fibonacci ini:
Ini berfungsi, tetapi bagaimana jika seseorang memberikan angka negatif ke fungsi Anda? Itu tidak akan berhasil kalau begitu! Jadi, Anda ingin menambahkan tanda centang untuk memastikan bahwa fungsi tersebut dipanggil dengan input non-negatif.
Mungkin tergoda untuk menulis fungsi seperti ini:
Namun, jika Anda melakukan ini, maka kemudian secara tidak sengaja memanggil fungsi Fibonacci Anda dengan input negatif, Anda tidak akan pernah menyadarinya! Lebih buruk lagi, program Anda mungkin akan terus berjalan tetapi mulai menghasilkan hasil yang tidak masuk akal tanpa memberi Anda petunjuk di mana ada sesuatu yang salah. Ini adalah jenis bug yang paling sulit untuk diperbaiki.
Sebagai gantinya, lebih baik menulis cek seperti ini:
Sekarang, jika Anda secara tidak sengaja memanggil fungsi Fibonacci dengan input negatif, program Anda akan segera berhenti dan memberi tahu Anda bahwa ada sesuatu yang salah. Selanjutnya, dengan memberi Anda jejak tumpukan, program akan memberi tahu Anda bagian mana dari program Anda yang mencoba mengeksekusi fungsi Fibonacci secara tidak benar, memberi Anda titik awal yang sangat baik untuk men-debug apa yang salah.
sumber
Haruskah Anda menambahkan kode yang berlebihan? Tidak.
Tapi, apa yang Anda gambarkan bukan kode yang mubazir .
Apa yang Anda gambarkan adalah pemrograman secara defensif terhadap kode panggilan yang melanggar prasyarat fungsi Anda. Apakah Anda melakukan ini atau hanya menyerahkannya kepada pengguna untuk membaca dokumentasi dan menghindari pelanggaran itu sendiri, sepenuhnya subjektif.
Secara pribadi, saya sangat percaya pada metodologi ini, tetapi seperti semua yang Anda harus berhati-hati. Ambil, misalnya, C ++
std::vector::operator[]
. Mengesampingkan implementasi mode debug VS untuk sesaat, fungsi ini tidak melakukan pemeriksaan batas. Jika Anda meminta elemen yang tidak ada, hasilnya tidak ditentukan. Terserah kepada pengguna untuk memberikan indeks vektor yang valid. Ini cukup disengaja: Anda dapat "memilih" untuk memeriksa batas dengan menambahkannya di callsite, tetapi jikaoperator[]
implementasinya melakukannya maka Anda tidak akan bisa "memilih keluar". Sebagai fungsi tingkat rendah, ini masuk akal.Tetapi jika Anda sedang menulis sebuah
AddEmployee(string name)
fungsi untuk beberapa antarmuka tingkat yang lebih tinggi, saya sepenuhnya berharap fungsi ini setidaknya melemparkan pengecualian jika Anda menyediakan kosongname
, serta prasyarat ini didokumentasikan segera di atas deklarasi fungsi. Anda mungkin tidak memberikan input pengguna yang tidak didiagnosis untuk fungsi ini hari ini, tetapi menjadikannya "aman" dengan cara ini berarti segala pelanggaran prasyarat yang muncul di masa depan dapat dengan mudah didiagnosis, daripada berpotensi memicu rantai domino kartu sulit yang harus dikerjakan. mendeteksi bug. Ini bukan redundansi: ini adalah ketekunan.Jika saya harus datang dengan aturan umum (walaupun, sebagai aturan umum, saya mencoba untuk menghindari itu), saya akan mengatakan bahwa fungsi yang memenuhi salah satu dari berikut ini:
... dapat mengambil manfaat dari pemrograman defensif. Dalam kasus lain, Anda masih dapat menulis
assert
ion yang menyala selama pengujian tetapi dinonaktifkan di build rilis, untuk lebih meningkatkan kemampuan Anda menemukan bug.Topik ini dieksplorasi lebih lanjut di Wikipedia ( https://en.wikipedia.org/wiki/Defensive_programming ).
sumber
Dua dari sepuluh perintah pemrograman relevan di sini:
Anda tidak akan berasumsi bahwa input benar
Jangan membuat kode untuk penggunaan di masa mendatang
Di sini, memeriksa null bukan "membuat kode untuk digunakan di masa mendatang". Membuat kode untuk digunakan di masa depan adalah hal-hal seperti menambahkan antarmuka karena Anda pikir mereka mungkin berguna "suatu hari". Dengan kata lain, perintahnya adalah untuk tidak menambahkan lapisan abstraksi kecuali dibutuhkan saat ini.
Memeriksa null tidak ada hubungannya dengan penggunaan di masa mendatang. Itu ada hubungannya dengan Perintah # 1: jangan menganggap input akan benar. Jangan pernah menganggap fungsi Anda akan menerima beberapa subset input. Suatu fungsi harus merespons dengan cara yang logis tidak peduli seberapa palsu dan kacau inputnya.
sumber
Thou shall not make code for future use
mengalami masalah pemeliharaan lebih cepat , terlepas dari logika intuitif perintah tersebut. Saya telah menemukan, dalam pengkodean kehidupan nyata, bahwa perintah hanya efektif dalam kode di mana Anda mengontrol daftar fitur dan tenggat waktu, dan pastikan Anda tidak memerlukan kode yang kelihatan di masa depan untuk mencapainya ... artinya, tidak pernah.Definisi 'redundant code' dan 'YAGNI' sering tergantung saya temukan pada seberapa jauh Anda melihat ke depan.
Jika Anda mengalami masalah, maka Anda cenderung menulis kode di masa depan sedemikian rupa untuk menghindari masalah itu. Programmer lain yang tidak mengalami masalah tertentu mungkin menganggap kode Anda berlebihan berlebihan.
Saran saya adalah untuk melacak berapa banyak waktu yang Anda habiskan untuk 'hal-hal yang belum keliru' jika bebannya dan Anda rekan-rekannya mengeluarkan fitur lebih cepat daripada Anda, kemudian menguranginya.
Namun, jika Anda seperti saya, saya berharap Anda akan mengetik semuanya 'secara default' dan itu tidak benar-benar membawa Anda lagi.
sumber
Merupakan ide bagus untuk mendokumentasikan asumsi tentang parameter. Dan itu ide yang baik untuk memeriksa bahwa kode klien Anda tidak melanggar asumsi tersebut. Saya akan melakukan ini:
[Dengan asumsi ini adalah C #, Pernyataan mungkin bukan alat terbaik untuk pekerjaan itu, karena tidak dikompilasi dalam kode yang dirilis. Tapi itu debat untuk hari lain.]
Mengapa ini lebih baik dari apa yang Anda tulis? Kode Anda masuk akal jika, di masa depan di mana klien Anda telah berubah, ketika klien melewati daftar kosong, hal yang benar untuk dilakukan adalah menambahkan baris pertama dan menambahkan aplikasi ke janji temu. Tetapi bagaimana Anda tahu bahwa itu akan terjadi? Lebih baik membuat asumsi sekarang tentang masa depan yang lebih sedikit.
sumber
Perkirakan biaya penambahan kode itu sekarang . Ini akan relatif murah karena semuanya segar di pikiran Anda, sehingga Anda dapat melakukan ini dengan cepat. Menambahkan tes unit diperlukan - tidak ada yang lebih buruk daripada menggunakan beberapa metode setahun kemudian, itu tidak berhasil, dan Anda tahu itu rusak sejak awal dan tidak pernah benar-benar bekerja.
Perkirakan biaya penambahan kode itu saat dibutuhkan. Ini akan lebih mahal, karena Anda harus kembali ke kode, mengingat semuanya, dan itu jauh lebih sulit.
Perkirakan probabilitas bahwa kode tambahan akan benar-benar dibutuhkan. Lalu lakukan matematika.
Di sisi lain, kode penuh dengan asumsi "X tidak akan pernah terjadi" sangat buruk untuk debugging. Jika sesuatu tidak berfungsi sebagaimana dimaksud, itu berarti kesalahan bodoh, atau asumsi yang salah. "X Anda tidak akan pernah terjadi" adalah asumsi, dan di hadapan bug itu mencurigakan. Yang memaksa pengembang berikutnya untuk membuang waktu di atasnya. Biasanya lebih baik tidak mengandalkan asumsi seperti itu.
sumber
Pertanyaan utama di sini adalah "apa yang akan terjadi jika Anda lakukan / tidak"?
Seperti yang telah ditunjukkan orang lain, pemrograman defensif semacam ini bagus, tetapi terkadang juga berbahaya.
Misalnya, jika Anda memberikan nilai default, maka Anda mempertahankan program. Tetapi program sekarang mungkin tidak melakukan apa yang Anda inginkan. Misalnya, jika ia menulis array kosong itu ke sebuah file, Anda sekarang mungkin telah mengubah bug Anda dari "crash karena saya memasok null secara tidak sengaja" menjadi "menghapus baris kalender karena saya memasok null secara tidak sengaja". (misalnya jika Anda mulai menghapus hal-hal yang tidak muncul dalam daftar di bagian yang bertuliskan "// blah")
Kuncinya bagi saya adalah Jangan pernah merusak data . Saya ulangi lagi; TIDAK PERNAH. KORUP. DATA. Jika pengecualian program Anda keluar, Anda mendapatkan laporan bug yang dapat Anda tambal; jika ia menulis data buruk ke file yang nantinya, Anda harus menabur garam.
Semua keputusan "tidak perlu" Anda harus dibuat dengan pemikiran itu.
sumber
Apa yang Anda hadapi di sini pada dasarnya adalah sebuah antarmuka. Dengan menambahkan perilaku "saat input
null
, inisialisasi input", Anda telah secara efektif memperluas antarmuka metode - sekarang alih-alih selalu beroperasi pada daftar yang valid, Anda telah membuatnya "memperbaiki" input. Apakah ini bagian resmi atau tidak resmi dari antarmuka, Anda dapat bertaruh seseorang (kemungkinan besar termasuk Anda) akan menggunakan perilaku ini.Antarmuka harus dijaga tetap sederhana, dan harus relatif stabil - terutama dalam hal seperti
public static
metode. Anda mendapatkan sedikit kelonggaran dalam metode pribadi, terutama metode contoh pribadi. Dengan memperluas antarmuka secara implisit, Anda telah membuat kode Anda lebih rumit dalam praktiknya. Sekarang, bayangkan Anda tidak benar - benar ingin menggunakan jalur kode itu - jadi Anda menghindarinya. Sekarang Anda punya sedikit kode yang belum diuji yang berpura - pura itu bagian dari perilaku metode. Dan saya dapat memberitahu Anda sekarang bahwa itu mungkin memiliki bug: ketika Anda melewati daftar, daftar itu dimutasi oleh metode. Namun, jika tidak, Anda membuat lokaldaftar, dan membuangnya nanti. Ini adalah jenis perilaku tidak konsisten yang akan membuat Anda menangis dalam waktu setengah tahun, ketika Anda mencoba melacak bug yang tidak jelas.Secara umum, pemrograman defensif adalah hal yang sangat berguna. Tetapi jalur kode untuk pemeriksaan defensif harus diuji, sama seperti kode lainnya. Dalam kasus seperti ini, mereka memperumit kode Anda tanpa alasan, dan saya akan memilih alternatif seperti ini sebagai gantinya:
Anda tidak ingin input
rows
yang null, dan Anda ingin membuat kesalahan jelas untuk semua penelepon Anda sesegera mungkin .Ada banyak nilai yang Anda butuhkan untuk menangani saat mengembangkan perangkat lunak. Bahkan ketahanan itu sendiri adalah kualitas yang sangat kompleks - misalnya, saya tidak akan menganggap pemeriksaan defensif Anda lebih kuat daripada melemparkan pengecualian. Pengecualian cukup berguna untuk memberi Anda tempat yang aman untuk mencoba lagi dari tempat yang aman - masalah korupsi data biasanya jauh lebih sulit dilacak daripada mengenali masalah sejak dini dan menanganinya dengan aman. Pada akhirnya, mereka hanya cenderung memberi Anda ilusi kekokohan, dan kemudian sebulan kemudian Anda melihat bahwa sepersepuluh janji Anda hilang, karena Anda tidak pernah memperhatikan bahwa daftar yang berbeda diperbarui. Aduh.
Pastikan untuk membedakan keduanya. Pemrograman defensif adalah teknik yang berguna untuk menangkap kesalahan di tempat yang paling relevan, sangat membantu upaya debugging Anda, dan dengan penanganan pengecualian yang baik, mencegah "korupsi licik". Gagal lebih awal, gagal cepat. Di sisi lain, apa yang Anda lakukan lebih seperti "menyembunyikan kesalahan" - Anda menyulap input dan membuat asumsi tentang apa yang dimaksud pemanggil. Ini sangat penting untuk kode yang menghadap pengguna (mis. Pengecekan ejaan), tetapi Anda harus waspada ketika melihat ini dengan kode yang menghadap pengembang.
Masalah utama adalah bahwa apa pun abstraksi yang Anda buat, itu akan bocor ("Saya ingin mengetik ulang, jangan kedepan! Pemeriksa ejaan bodoh!"), Dan kode untuk menangani semua kasus khusus dan perbaikan masih menjadi kode Anda perlu mempertahankan dan memahami, dan kode yang perlu Anda uji. Bandingkan upaya untuk memastikan daftar yang tidak nol dilewatkan dengan memperbaiki bug yang Anda miliki setahun kemudian dalam produksi - itu bukan trade-off yang baik. Di dunia yang ideal, Anda ingin setiap metode bekerja secara eksklusif dengan inputnya sendiri, mengembalikan hasilnya dan tidak mengubah keadaan global. Tentu saja, di dunia nyata, Anda akan menemukan banyak kasus di mana itu tidaksolusi paling sederhana dan paling jelas (misalnya saat menyimpan file), tetapi saya menemukan bahwa menjaga metode "murni" ketika tidak ada alasan bagi mereka untuk membaca atau memanipulasi keadaan global membuat kode lebih mudah untuk dipikirkan. Ini juga cenderung memberi Anda lebih banyak poin alami untuk memisahkan metode Anda :)
Ini tidak berarti bahwa segala sesuatu yang tidak terduga akan membuat aplikasi Anda mogok, sebaliknya. Jika Anda menggunakan pengecualian dengan baik, mereka secara alami membentuk titik penanganan kesalahan yang aman, di mana Anda dapat memulihkan keadaan aplikasi yang stabil dan memungkinkan pengguna untuk melanjutkan apa yang mereka lakukan (idealnya sambil menghindari kehilangan data bagi pengguna). Pada titik-titik penanganan itu, Anda akan melihat peluang untuk memperbaiki masalah ("Tidak ditemukan nomor urut 2212. Apakah maksud Anda 2212b?") Atau memberikan kontrol pengguna ("Kesalahan menyambung ke basis data. Coba lagi?"). Bahkan ketika tidak ada pilihan seperti itu tersedia, setidaknya itu akan memberi Anda kesempatan bahwa tidak ada yang rusak - Saya sudah mulai menghargai kode yang menggunakan
using
dantry
...finally
lebih daritry
...catch
, ini memberi Anda banyak peluang untuk mempertahankan invarian bahkan dalam kondisi luar biasa.Pengguna seharusnya tidak kehilangan data dan pekerjaan mereka. Ini masih harus diimbangi dengan biaya pengembangan dll., Tetapi itu adalah pedoman umum yang cukup bagus (jika pengguna memutuskan apakah akan membeli perangkat lunak Anda atau tidak - perangkat lunak internal biasanya tidak memiliki kemewahan itu). Bahkan seluruh aplikasi mogok menjadi jauh lebih sedikit dari masalah jika pengguna hanya dapat me-restart dan kembali ke apa yang mereka lakukan. Ini adalah kekuatan yang nyata - Word menyimpan pekerjaan Anda sepanjang waktu tanpa merusak dokumen Anda pada disk, dan memberi Anda opsiuntuk mengembalikan perubahan itu setelah me-restart Word setelah crash. Apakah lebih baik daripada tidak ada bug di tempat pertama? Mungkin tidak - meskipun jangan lupa bahwa pekerjaan yang dihabiskan untuk menangkap bug langka mungkin lebih baik dihabiskan di mana-mana. Tapi itu jauh lebih baik daripada alternatif - misalnya dokumen yang rusak pada disk, semua pekerjaan sejak terakhir disimpan hilang, dokumen otomatis diganti dengan perubahan sebelum crash yang kebetulan adalah Ctrl + A dan Delete.
sumber
Saya akan menjawab ini berdasarkan asumsi Anda bahwa kode yang kuat akan menguntungkan Anda "tahun" dari sekarang. Jika manfaat jangka panjang adalah tujuan Anda, saya akan memprioritaskan desain dan pemeliharaan dibandingkan ketahanan.
Pertukaran antara desain dan ketahanan, adalah waktu dan fokus. Sebagian besar pengembang lebih suka memiliki satu set kode yang dirancang dengan baik bahkan jika itu berarti melalui beberapa titik masalah dan melakukan beberapa kondisi tambahan atau penanganan kesalahan. Setelah beberapa tahun digunakan, tempat-tempat yang benar-benar Anda butuhkan mungkin telah diidentifikasi oleh pengguna.
Dengan asumsi desain memiliki kualitas yang sama, kode yang lebih sedikit lebih mudah dipelihara. Ini tidak berarti kita lebih baik jika Anda membiarkan masalah yang diketahui pergi selama beberapa tahun, tetapi menambahkan hal-hal yang Anda tahu tidak perlu menyulitkan. Kita semua telah melihat kode lama dan menemukan bagian yang tidak perlu. Anda harus memiliki kode perubahan kepercayaan tingkat tinggi yang telah bekerja selama bertahun-tahun.
Jadi, jika Anda merasa aplikasi Anda dirancang sebaik mungkin, mudah dirawat dan tidak memiliki bug, temukan sesuatu yang lebih baik untuk dilakukan daripada menambahkan kode yang tidak Anda butuhkan. Paling tidak itu bisa Anda lakukan untuk menghormati semua pengembang lain yang bekerja berjam-jam pada fitur yang tidak berguna.
sumber
Tidak seharusnya tidak. Dan Anda benar-benar menjawab pertanyaan Anda sendiri ketika Anda menyatakan bahwa cara pengkodean ini dapat menyembunyikan bug . Ini tidak akan membuat kode lebih kuat - melainkan akan membuatnya lebih rentan terhadap bug dan membuat debugging lebih sulit.
Anda menyatakan harapan Anda saat ini tentang
rows
argumen: Entah itu nol atau memiliki setidaknya satu item. Jadi pertanyaannya adalah: Apakah ide yang baik untuk menulis kode untuk menangani kasus ketiga yang tidak terduga di manarows
nol item?Jawabannya adalah tidak. Anda harus selalu memberikan pengecualian dalam hal input yang tidak terduga. Pertimbangkan ini: Jika beberapa bagian lain dari kode melanggar harapan (yaitu kontrak) dari metode Anda itu berarti ada bug . Jika ada bug yang ingin Anda ketahui sedini mungkin sehingga Anda dapat memperbaikinya, dan pengecualian akan membantu Anda melakukannya.
Apa yang dilakukan kode saat ini adalah menebak bagaimana memulihkan dari bug yang mungkin ada atau tidak ada dalam kode tersebut. Tetapi bahkan jika ada bug, Anda tidak tahu bagaimana memulihkan sepenuhnya dari itu. Bug menurut definisi memiliki konsekuensi yang tidak diketahui. Mungkin beberapa kode inisialisasi tidak berjalan seperti yang diharapkan mungkin memiliki banyak konsekuensi selain dari baris yang hilang.
Jadi kode Anda akan terlihat seperti ini:
Catatan: Ada beberapa kasus khusus di mana masuk akal untuk "menebak" bagaimana menangani input yang tidak valid daripada hanya melemparkan pengecualian. Misalnya, jika Anda menangani input eksternal, Anda tidak dapat mengendalikannya. Browser web adalah contoh yang terkenal karena mereka mencoba untuk menangani segala jenis input yang salah dan tidak valid. Tetapi ini hanya masuk akal dengan input eksternal, bukan dengan panggilan dari bagian lain dari program.
Sunting: Beberapa jawaban lain menyatakan bahwa Anda melakukan pemrograman defensif . Saya tidak setuju. Pemrograman defensif berarti Anda tidak secara otomatis memercayai input yang valid. Jadi validasi parameter (seperti di atas) adalah teknik pemrograman defensif, tetapi itu tidak berarti Anda harus mengubah input yang tidak terduga atau tidak valid dengan menebak. Pendekatan defensif yang kuat adalah untuk memvalidasi input dan kemudian melemparkan pengecualian jika input yang tidak terduga atau tidak valid.
sumber
Anda tidak boleh menambahkan kode berlebihan kapan saja.
Anda tidak boleh menambahkan kode yang hanya dibutuhkan di masa mendatang.
Anda harus memastikan bahwa kode Anda berperilaku baik apa pun yang terjadi.
Definisi "berperilaku baik" sesuai dengan kebutuhan Anda. Salah satu teknik yang saya suka gunakan adalah pengecualian "paranoia". Jika saya 100% yakin bahwa kasus tertentu tidak akan pernah terjadi, saya masih memprogram pengecualian, tetapi saya melakukannya dengan cara yang a) dengan jelas memberi tahu semua orang bahwa saya tidak pernah berharap ini terjadi, dan b) ditampilkan dengan jelas dan dicatat dengan demikian tidak menyebabkan korupsi yang menjalar di kemudian hari.
Contoh pseudo-code:
Ini dengan jelas mengomunikasikan bahwa saya 100% yakin bahwa saya selalu dapat membuka, menulis, atau menutup file, yaitu, saya tidak pergi sampai batas membuat penanganan kesalahan yang rumit. Tetapi jika (tidak: kapan) saya tidak dapat membuka file, program saya masih akan gagal secara terkendali.
Antarmuka pengguna tentu saja tidak akan menampilkan pesan tersebut kepada pengguna, mereka akan dicatat secara internal bersama dengan jejak tumpukan. Sekali lagi, ini adalah pengecualian "Paranoia" internal yang hanya memastikan bahwa kode "berhenti" ketika sesuatu yang tidak terduga terjadi. Contoh ini agak dirundung, dalam praktiknya saya tentu saja akan menerapkan penanganan kesalahan nyata untuk kesalahan saat membuka file, karena ini terjadi secara teratur (nama file yang salah, USB stick mount read-only, apa pun).
Istilah pencarian terkait yang sangat penting adalah "gagal cepat", sebagaimana dicatat dalam jawaban lain dan sangat berguna untuk membuat perangkat lunak yang tangguh.
sumber
Ada banyak jawaban yang terlalu rumit di sini. Anda mungkin bertanya pertanyaan ini karena Anda merasa tidak benar tentang kode bagian itu tetapi tidak yakin mengapa atau bagaimana cara memperbaikinya. Jadi jawaban saya adalah bahwa masalahnya sangat mungkin dalam struktur kode (seperti biasa).
Pertama, header metode:
Tetapkan janji temu untuk baris apa ? Itu harus segera dihapus dari daftar parameter. Tanpa pengetahuan lebih lanjut, saya harapkan metode params terlihat seperti ini:
(Appointment app, CalendarRow row)
.Selanjutnya, "input check":
Ini omong kosong.
rows
ke metode mungkin salah (lihat komentar di atas), maka seharusnya tidak menjadi tanggung jawab dari metode yang dipanggilAssignAppointmentToRow
untuk memanipulasi baris dengan cara lain selain menugaskan janji di suatu tempat.Tetapi seluruh konsep menugaskan suatu tempat aneh (kecuali jika ini adalah bagian dari kode GUI). Kode Anda tampaknya mengandung (atau setidaknya itu mencoba) suatu struktur data eksplisit yang mewakili kalender (yaitu
List<CalendarRows>
<- yang harus didefinisikan sebagaiCalendar
suatu tempat jika Anda ingin pergi dengan cara ini, maka Anda akan meneruskanCalendar calendar
ke metode Anda). Jika Anda pergi dengan cara ini, saya berharapcalendar
akan diawali dengan slot di mana Anda menempatkan (menetapkan) janji sesudahnya (misalnyacalendar[month][day] = appointment
akan menjadi kode yang sesuai). Tapi kemudian Anda juga dapat memilih untuk membuang struktur kalender dari logika utama sekaligus dan hanya memilikiList<Appointment>
tempatAppointment
objek berisi atributdate
. Dan kemudian jika Anda perlu membuat kalender di suatu tempat di GUI, Anda dapat membuat struktur 'kalender eksplisit' ini sebelum merender.Saya tidak tahu detail aplikasi Anda sehingga beberapa di antaranya mungkin tidak berlaku untuk Anda tetapi kedua pemeriksaan ini (terutama yang kedua) memberi tahu saya bahwa ada sesuatu yang salah dengan pemisahan masalah dalam kode Anda.
sumber
Untuk kesederhanaan, mari kita asumsikan bahwa Anda pada akhirnya akan membutuhkan kode ini dalam N hari (tidak lebih lambat atau lebih awal) atau tidak perlu sama sekali.
Kodesemu:
Faktor-faktor untuk
C_maint
:C_maint
diharapkan negatifC_maint
. Kasus ini memerlukan rumus yang lebih kompleks, dengan variabel yang lebih sering sukaN
.Begitu besar hal yang hanya memberatkan kode dan mungkin diperlukan hanya dalam 2 tahun dengan probabilitas rendah harus ditinggalkan, tetapi hal kecil yang juga menghasilkan pernyataan yang berguna dan 50% bahwa itu akan diperlukan dalam 3 bulan, itu harus dilaksanakan .
sumber