Jika Anda harus memilih teknik Favorit (pandai) Anda untuk pengkodean defensif, akan seperti apa mereka? Meskipun bahasa saya saat ini adalah Java dan Objective-C (dengan latar belakang dalam C ++), jangan ragu untuk menjawab dalam bahasa apa pun. Penekanan di sini adalah pada teknik pertahanan pintar selain dari yang sudah kita ketahui 70% lebih dari kita. Jadi sekarang saatnya untuk menggali lebih dalam ke dalam tas trik Anda.
Dengan kata lain cobalah untuk memikirkan selain dari contoh yang tidak menarik ini :
if(5 == x)
alih-alihif(x == 5)
: untuk menghindari penugasan yang tidak diinginkan
Berikut adalah beberapa contoh beberapa praktik pemrograman defensif terbaik yang menarik (contoh spesifik bahasa ada di Jawa):
- Kunci variabel Anda sampai Anda tahu bahwa Anda perlu mengubahnya
Artinya, Anda dapat mendeklarasikan semua variabel final
hingga Anda tahu bahwa Anda perlu mengubahnya, pada titik mana Anda dapat menghapus final
. Satu fakta yang umumnya tidak diketahui adalah bahwa ini juga valid untuk params metode:
public void foo(final int arg) { /* Stuff Here */ }
- Ketika sesuatu yang buruk terjadi, tinggalkan jejak bukti
Ada beberapa hal yang dapat Anda lakukan ketika Anda memiliki pengecualian: jelas mencatatnya dan melakukan pembersihan akan sedikit. Tetapi Anda juga dapat meninggalkan jejak bukti (mis. Menyetel variabel ke nilai sentinel seperti "UNABLE TO LOAD FILE" atau 99999 akan berguna dalam debugger, jika Anda kebetulan melewati pengecualian catch
-block).
- Ketika sampai pada konsistensi: iblis ada dalam perinciannya
Konsisten dengan perpustakaan lain yang Anda gunakan. Misalnya, di Jawa, jika Anda membuat metode yang mengekstrak rentang nilai, buat inklusif batas bawah dan eksklusif batas atas . Ini akan membuatnya konsisten dengan metode seperti String.substring(start, end)
yang beroperasi dengan cara yang sama. Anda akan menemukan semua jenis metode ini di Sun JDK untuk berperilaku seperti ini karena membuat berbagai operasi termasuk iterasi elemen yang konsisten dengan array, di mana indeks mulai dari Nol ( inklusif ) hingga panjang array ( eksklusif ).
Jadi, apa saja praktik defensif favorit Anda?
Pembaruan: Jika Anda belum melakukannya, jangan ragu untuk berpadu. Saya memberikan kesempatan untuk mendapat lebih banyak tanggapan sebelum saya memilih jawaban resmi .
sumber
Jawaban:
Dalam c ++, saya pernah suka mendefinisikan ulang baru sehingga memberikan beberapa memori tambahan untuk menangkap kesalahan posting pagar.
Saat ini, saya lebih suka menghindari pemrograman defensif yang mendukung Test Driven Development . Jika Anda menangkap kesalahan dengan cepat dan eksternal, Anda tidak perlu memperkeruh kode Anda dengan manuver defensif, kode Anda KERING -an dan Anda berakhir dengan lebih sedikit kesalahan yang harus Anda pertahankan.
Seperti Tulisan WikiKnowledge :
sumber
SQL
Ketika saya harus menghapus data, saya menulis
Ketika saya menjalankannya, saya akan tahu jika saya lupa atau merusak klausa mana. Saya memiliki keamanan. Jika semuanya baik-baik saja, saya sorot semuanya setelah tanda komentar '-', dan jalankan.
Sunting: jika saya menghapus banyak data, saya akan menggunakan count (*) alih-alih hanya *
sumber
Mengalokasikan sepotong memori yang masuk akal ketika aplikasi dimulai - saya pikir Steve McConnell menyebutnya sebagai parasut memori dalam Kode Lengkap.
Ini dapat digunakan jika terjadi kesalahan serius dan Anda diharuskan untuk mengakhiri.
Mengalokasikan memori ini di muka memberi Anda jaring pengaman, karena Anda dapat membebaskannya dan kemudian menggunakan memori yang tersedia untuk melakukan hal berikut:
sumber
Di setiap pernyataan beralih yang tidak memiliki case default, saya menambahkan case yang membatalkan program dengan pesan kesalahan.
sumber
Saat Anda menangani berbagai status enum (C #):
Kemudian, di dalam beberapa rutinitas ...
Pada titik tertentu saya akan menambahkan jenis akun lain ke enum ini. Dan ketika saya melakukannya, saya akan lupa untuk memperbaiki pernyataan switch ini. Jadi
Debug.Fail
crash mengerikan (dalam mode Debug) untuk menarik perhatian saya pada fakta ini. Ketika saya menambahkancase AccountType.MyNewAccountType:
, crash mengerikan berhenti ... sampai saya menambahkan jenis akun lain dan lupa untuk memperbarui kasus di sini.(Ya, polimorfisme mungkin lebih baik di sini, tetapi ini hanya contoh dari atas kepala saya.)
sumber
Saat mencetak pesan kesalahan dengan string (terutama yang tergantung pada input pengguna), saya selalu menggunakan tanda kutip tunggal
''
. Sebagai contoh:Kurangnya kutipan di sekitar
%s
ini benar-benar buruk, karena katakan nama file adalah string kosong atau hanya spasi putih atau sesuatu. Pesan yang dicetak tentu saja adalah:Jadi, selalu lebih baik untuk dilakukan:
Maka setidaknya pengguna melihat ini:
Saya menemukan bahwa ini membuat perbedaan besar dalam hal kualitas laporan bug yang dikirimkan oleh pengguna akhir. Jika ada pesan kesalahan yang tampak lucu seperti ini alih-alih sesuatu yang terdengar umum, maka mereka lebih mungkin untuk menyalin / menempelkannya daripada hanya menulis "itu tidak akan membuka file saya".
sumber
Keamanan SQL
Sebelum menulis SQL apa pun yang akan mengubah data, saya membungkus semuanya dalam transaksi yang dibatalkan:
Ini mencegah Anda dari melakukan penghapusan / pembaruan buruk secara permanen. Dan, Anda dapat menjalankan semuanya dan memverifikasi jumlah catatan yang masuk akal atau menambahkan
SELECT
pernyataan antara SQL Anda danROLLBACK TRANSACTION
untuk memastikan semuanya terlihat benar.Ketika Anda benar-benar yakin itu melakukan apa yang Anda harapkan, ubah
ROLLBACK
toCOMMIT
dan jalankan nyata.sumber
Untuk semua bahasa:
Kurangi cakupan variabel hingga seminimal mungkin diperlukan. Hindari variabel yang hanya disediakan untuk membawanya ke pernyataan berikutnya. Variabel yang tidak ada adalah variabel yang tidak perlu Anda pahami, dan Anda tidak dapat dianggap bertanggung jawab. Gunakan Lambdas jika memungkinkan untuk alasan yang sama.
sumber
Jika ragu, bom aplikasi!
Periksa masing-masing dan setiap parameter di awal masing - masing dan setiap metode (apakah secara eksplisit mengkode sendiri, atau menggunakan pemrograman berbasis kontrak tidak masalah di sini) dan bom dengan pengecualian yang benar dan / atau pesan kesalahan yang bermakna jika ada prasyarat untuk kode adalah tidak bertemu.
Kita semua tahu tentang prasyarat implisit ini ketika kita menulis kode , tetapi jika mereka tidak dicek secara eksplisit, kita menciptakan labirin untuk diri kita sendiri ketika ada yang tidak beres kemudian dan tumpukan lusinan pemanggilan metode memisahkan kejadian gejala dan lokasi sebenarnya. di mana prasyarat tidak terpenuhi (= di mana sebenarnya masalah / bug).
sumber
param.NotNull("param")
bukanif ( param == null ) throw new ArgumentNullException("param");
Di Jawa, terutama dengan koleksi, gunakan API, jadi jika metode Anda mengembalikan tipe daftar (misalnya), coba yang berikut ini:
Jangan biarkan apa pun keluar dari kelas Anda yang tidak perlu Anda lakukan!
sumber
di Perl, semua orang melakukannya
saya suka
Ini menyebabkan kode mati untuk peringatan kompiler / runtime. Ini sebagian besar berguna dalam menangkap string yang tidak diinisialisasi.
sumber
C #:
sumber
Dalam c # memeriksa string.IsNullOrEmpty sebelum melakukan operasi pada string seperti panjang, indexOf, pertengahan dll
[Sunting]
Sekarang saya juga bisa melakukan hal berikut dalam. NET 4.0, yang juga memeriksa apakah nilainya hanya spasi kosong
sumber
if (!string.IsNullOrEmpty(myString)) throw new ArgumentException("<something>", "myString"); /* do something with string */
.Di Jawa dan C #, beri setiap utas nama yang bermakna. Ini termasuk utas kumpulan benang. Itu membuat tumpukan dump lebih bermakna. Dibutuhkan sedikit usaha lebih untuk memberikan nama yang berarti bahkan untuk utas utas, tetapi jika satu utas utas memiliki masalah dalam aplikasi yang berjalan lama, saya dapat menyebabkan tumpukan tumpukan terjadi (Anda tahu tentang SendSignal.exe , kan? ), ambil log, dan tanpa harus mengganggu sistem yang sedang berjalan saya dapat mengetahui utas mana ... apa pun. Jalan buntu, bocor, tumbuh, apa pun masalahnya.
sumber
Dengan VB.NET, aktifkan Option Explicit dan Option Strict secara default untuk seluruh Visual Studio.
sumber
C ++
lalu ganti semua panggilan ' hapus pPtr ' dan ' hapus [] pPtr ' Anda dengan SAFE_DELETE (pPtr) dan SAFE_DELETE_ARRAY (pPtr)
Sekarang karena kesalahan jika Anda menggunakan pointer 'pPtr' setelah menghapusnya, Anda akan mendapatkan kesalahan 'akses pelanggaran'. Jauh lebih mudah untuk memperbaikinya daripada kerusakan memori acak.
sumber
delete
. Menggunakannew
tidak apa-apa, asalkan objek baru dimiliki oleh pointer pintar.Dengan Java, akan sangat berguna untuk menggunakan kata kunci yang menegaskan, bahkan jika Anda menjalankan kode produksi dengan pernyataan yang dimatikan:
Bahkan dengan pernyataan tidak aktif, setidaknya kode mendokumentasikan fakta bahwa param diharapkan akan ditetapkan di suatu tempat. Perhatikan bahwa ini adalah fungsi pembantu pribadi dan bukan anggota API publik. Metode ini hanya bisa dipanggil oleh Anda, jadi tidak apa-apa untuk membuat asumsi tertentu tentang bagaimana itu akan digunakan. Untuk metode publik, mungkin lebih baik untuk melemparkan pengecualian nyata untuk input yang tidak valid.
sumber
Saya tidak menemukan
readonly
kata kunci sampai saya menemukan ReSharper, tetapi sekarang saya menggunakannya secara naluriah, terutama untuk kelas layanan.sumber
Di Jawa, ketika sesuatu terjadi dan saya tidak tahu mengapa, saya kadang-kadang akan menggunakan Log4J seperti ini:
dengan cara ini saya mendapatkan jejak stack yang menunjukkan kepada saya bagaimana saya masuk ke situasi yang tidak terduga, katakan kunci yang tidak pernah dibuka, sesuatu yang nol yang tidak boleh nol, dan sebagainya. Jelas, jika Pengecualian nyata dilemparkan, saya tidak perlu melakukan ini. Inilah saatnya saya perlu melihat apa yang terjadi dalam kode produksi tanpa benar-benar mengganggu yang lain. Saya tidak ingin membuang Exception dan saya tidak menangkapnya. Saya hanya ingin jejak stack dicatat dengan pesan yang sesuai untuk menandai saya apa yang terjadi.
sumber
Jika Anda menggunakan Visual C ++, gunakan kata kunci override setiap kali Anda naik metode kelas dasar. Dengan cara ini jika ada orang yang mengubah tanda tangan kelas dasar, itu akan menimbulkan kesalahan kompiler daripada metode yang salah dipanggil secara diam-diam. Ini akan menyelamatkan saya beberapa kali jika sudah ada sebelumnya.
Contoh:
sumber
Saya telah belajar di Jawa untuk hampir tidak pernah menunggu untuk membuka kunci tanpa batas waktu, kecuali jika saya benar-benar berharap bahwa itu akan memakan waktu yang lama. Jika secara realistis, kunci akan terbuka dalam hitungan detik, maka saya hanya akan menunggu untuk jangka waktu tertentu. Jika kunci tidak membuka, maka saya mengeluh dan membuang tumpukan ke log, dan tergantung pada apa yang terbaik untuk stabilitas sistem, baik melanjutkan seolah-olah kunci tidak terkunci, atau melanjutkan seolah-olah kunci tidak pernah membuka.
Ini telah membantu mengisolasi beberapa kondisi balapan dan kondisi buntu pseudo yang misterius sebelum saya mulai melakukan ini.
sumber
C #
sealed
banyak untuk kelas untuk menghindari memperkenalkan dependensi di mana saya tidak menginginkannya. Mengizinkan pewarisan harus dilakukan secara eksplisit dan bukan karena kecelakaan.sumber
Saat Anda mengeluarkan pesan kesalahan, setidaknya cobalah memberikan informasi yang sama dengan yang dimiliki program ketika membuat keputusan untuk membuang kesalahan.
"Izin ditolak" memberi tahu Anda ada masalah izin, tetapi Anda tidak tahu mengapa atau di mana masalah terjadi. "Tidak dapat menulis log transaksi / file saya: Sistem file read-only" setidaknya memberi tahu Anda dasar pengambilan keputusan, bahkan jika itu salah - terutama jika itu salah: nama file yang salah? salah dibuka? kesalahan tak terduga lainnya? - dan memberi tahu Anda di mana Anda berada saat Anda memiliki masalah.
sumber
Di C #, gunakan
as
kata kunci untuk melakukan cast.akan melempar pengecualian jika obj bukan string
akan meninggalkan sebagai null jika obj bukan string
Anda masih perlu mempertimbangkan null, tetapi itu biasanya lebih lurus ke depan daripada mencari pengecualian pemain. Kadang-kadang Anda ingin "melemparkan atau meledakkan" tipe perilaku, dalam hal ini
(string)obj
sintaksis lebih disukai.Dalam kode saya sendiri, saya menemukan saya menggunakan
as
sintaksis sekitar 75% dari waktu, dan(cast)
sintaksis sekitar 25%.sumber
is/instanceof
muncul dalam pikiran.Bersiaplah untuk input apa pun , dan input apa pun yang Anda dapatkan yang tidak terduga, buang ke log. (Sesuai alasan. Jika Anda membaca kata sandi dari pengguna, jangan membuangnya ke log! Dan jangan mencatat ribuan jenis pesan ini ke log per detik. Alasan tentang konten dan kemungkinan serta frekuensi sebelum Anda login) .)
Saya tidak hanya berbicara tentang validasi input pengguna. Misalnya, jika Anda membaca permintaan HTTP yang Anda harapkan mengandung XML, bersiaplah untuk format data lainnya. Saya terkejut melihat respons HTML di mana saya hanya mengharapkan XML - sampai saya melihat dan melihat bahwa permintaan saya melalui proxy transparan yang tidak saya sadari dan bahwa pelanggan mengklaim ketidaktahuan tentang - dan proxy kehabisan waktu mencoba menyelesaikan permintaan. Dengan demikian proxy mengembalikan halaman kesalahan HTML ke klien saya, membingungkan heck out dari klien yang hanya mengharapkan data XML.
Dengan demikian, bahkan ketika Anda berpikir Anda mengontrol kedua ujung kabel, Anda bisa mendapatkan format data yang tidak terduga tanpa terlibat kejahatan. Bersiaplah, buat kode secara defensif, dan berikan hasil diagnostik jika input tidak terduga.
sumber
Saya mencoba menggunakan pendekatan Design by Contract. Itu dapat ditiru waktu menjalankan oleh bahasa apa pun. Setiap bahasa mendukung "menegaskan", tetapi mudah dan covenient untuk menulis implementasi yang lebih baik yang memungkinkan Anda mengelola kesalahan dengan cara yang lebih bermanfaat.
Dalam 25 Kesalahan Pemrograman Paling Berbahaya Top , "Validasi Input Tidak Benar" adalah kesalahan paling berbahaya di bagian "Interaksi Tidak Aman Antar Komponen".
Menambahkan pernyataan prasyarat di awal metode adalah cara yang baik untuk memastikan bahwa parameter konsisten. Pada akhir metode saya menulis postconditions , yang memeriksa output apa yang diinginkan.
Untuk mengimplementasikan invarian , saya menulis sebuah metode di kelas mana saja yang memeriksa "konsistensi kelas", yang harus disebut secara otomatis oleh makro prekondisi dan postkondisi.
Saya mengevaluasi Perpustakaan Kontrak Kode .
sumber
Jawa
Java api tidak memiliki konsep objek yang tidak dapat diubah, yang buruk! Final dapat membantu Anda dalam hal itu. Tag setiap kelas yang berubah dengan akhir dan mempersiapkan kelas sesuai .
Terkadang berguna untuk menggunakan final pada variabel lokal untuk memastikan mereka tidak pernah mengubah nilainya. Saya menemukan ini berguna dalam jelek, tetapi diperlukan konstruksi lingkaran. Itu hanya untuk mudah menggunakan kembali variabel secara tidak sengaja meskipun itu menjadi konstan.
Gunakan menyalin pertahanan di getter Anda. Kecuali Anda mengembalikan tipe primitif atau objek yang tidak dapat diubah, pastikan Anda menyalin objek tersebut agar tidak melanggar enkapsulasi.
Jangan pernah menggunakan klon, gunakan copy constructor .
Pelajari kontrak antara equals dan kode hash. Ini sangat sering dilanggar. Masalahnya adalah itu tidak mempengaruhi kode Anda dalam 99% kasus. Orang menimpa sama, tetapi tidak peduli kode hash. Ada beberapa contoh di mana kode Anda dapat merusak atau berperilaku aneh, misalnya menggunakan objek yang bisa berubah sebagai kunci di peta.
sumber
Saya lupa menulis
echo
dalam PHP terlalu sering:Butuh waktu selamanya untuk mencoba dan mencari tahu mengapa -> baz () tidak mengembalikan apa-apa padahal sebenarnya aku tidak menggemakannya! : -S Jadi saya membuat
EchoMe
kelas yang dapat melilit nilai apa pun yang harus digaungkan:Dan kemudian untuk lingkungan pengembangan, saya menggunakan EchoMe untuk membungkus hal-hal yang harus dicetak:
Menggunakan teknik itu, contoh pertama yang hilang
echo
sekarang akan melempar pengecualian ...sumber
AnObject.ToString
bukanWriteln(AnObject.ToString)
?C ++
Saat saya mengetik baru, saya harus segera mengetik hapus. Khusus untuk array.
C #
Periksa nol sebelum mengakses properti, terutama ketika menggunakan pola Mediator. Objek dilewatkan (dan kemudian harus dilemparkan menggunakan seperti, sebagaimana telah dicatat), dan kemudian periksa terhadap nol. Bahkan jika Anda berpikir itu tidak akan menjadi nol, tanyakan pula. Saya terkejut.
sumber
Gunakan sistem pencatatan yang memungkinkan penyesuaian tingkat log waktu berjalan yang dinamis. Seringkali jika Anda harus menghentikan program untuk mengaktifkan pencatatan, Anda akan kehilangan kondisi langka apa pun yang terjadi. Anda harus dapat mengaktifkan lebih banyak informasi pencatatan tanpa menghentikan prosesnya.
Juga, 'strace -p [pid]' di linux akan menunjukkan Anda menginginkan pemanggilan sistem yang sedang diproses (atau utas linux). Awalnya mungkin terlihat aneh, tetapi begitu Anda terbiasa dengan panggilan sistem apa yang biasanya dilakukan oleh panggilan libc, Anda akan menemukan ini sangat berharga untuk diagnosis lapangan.
sumber