tetapi menabrak perangkat lunak klien Anda masih bukan hal yang baik
Ini pastinya adalah hal yang baik.
Anda ingin apa pun yang membuat sistem dalam keadaan tidak terdefinisi untuk menghentikan sistem karena sistem yang tidak terdefinisi dapat melakukan hal-hal buruk seperti data yang korup, memformat hard drive, dan mengirim presiden mengancam email. Jika Anda tidak dapat memulihkan dan mengembalikan sistem ke kondisi yang ditentukan, maka menabrak adalah hal yang harus dilakukan. Itulah tepatnya mengapa kita membangun sistem yang crash daripada diam-diam memisahkan diri. Sekarang tentu saja, kita semua menginginkan sistem stabil yang tidak pernah crash tetapi kami hanya benar-benar menginginkannya ketika sistem tetap dalam kondisi aman yang dapat diprediksi yang telah ditentukan.
Saya pernah mendengar bahwa pengecualian sebagai aliran kontrol dianggap sebagai antipattern yang serius
Itu benar sekali tetapi sering disalahpahami. Ketika mereka menemukan sistem pengecualian, mereka takut melanggar pemrograman terstruktur. Pemrograman terstruktur adalah mengapa kita memiliki for
, while
, until
, break
, dan continue
ketika semua yang kita butuhkan, untuk melakukan semua itu, adalah goto
.
Dijkstra mengajarkan kepada kita bahwa menggunakan goto secara informal (yaitu, melompat-lompat di mana pun Anda suka) membuat membaca kode menjadi mimpi buruk. Ketika mereka memberi kami sistem pengecualian, mereka takut menciptakan kembali kebagian. Jadi mereka mengatakan kepada kami untuk tidak "menggunakannya untuk kontrol aliran" berharap kami akan mengerti. Sayangnya, banyak dari kita tidak.
Anehnya, kita tidak sering menyalahgunakan pengecualian untuk membuat kode spageti seperti yang biasa kita lakukan dengan goto. Saran itu sendiri tampaknya telah menyebabkan lebih banyak masalah.
Pengecualian pada dasarnya adalah tentang menolak asumsi. Ketika Anda meminta agar suatu file disimpan, Anda menganggap bahwa file tersebut dapat dan akan disimpan. Pengecualian yang Anda dapatkan ketika itu tidak mungkin tentang nama yang ilegal, HD yang penuh, atau karena tikus telah menggerogoti kabel data Anda. Anda dapat menangani semua kesalahan itu secara berbeda, Anda dapat menangani semuanya dengan cara yang sama, atau Anda dapat membiarkannya menghentikan sistem. Ada jalur bahagia di kode Anda di mana asumsi Anda harus benar. Salah satu cara atau pengecualian lain membawa Anda keluar dari jalan bahagia itu. Sebenarnya, ya itu semacam "kontrol aliran" tapi bukan itu yang mereka peringatkan. Mereka berbicara tentang omong kosong seperti ini :
"Pengecualian harus luar biasa". Tautologi kecil ini lahir karena perancang sistem pengecualian membutuhkan waktu untuk membangun jejak tumpukan. Dibandingkan dengan melompat-lompat, ini lambat. Itu memakan waktu CPU. Tetapi jika Anda akan log dan menghentikan sistem atau setidaknya menghentikan pemrosesan intensif waktu saat ini sebelum memulai yang berikutnya maka Anda memiliki waktu untuk mematikan. Jika orang mulai menggunakan pengecualian "untuk kontrol aliran" asumsi-asumsi tentang waktu semua keluar jendela. Jadi "Pengecualian harus luar biasa" benar-benar diberikan kepada kami sebagai pertimbangan kinerja.
Jauh lebih penting daripada itu tidak membingungkan kita. Berapa lama waktu yang Anda butuhkan untuk menemukan infinite loop pada kode di atas?
JANGAN mengembalikan kode kesalahan.
... adalah saran yang bagus ketika Anda berada di basis kode yang biasanya tidak menggunakan kode kesalahan. Mengapa? Karena tidak ada yang akan mengingat untuk menyimpan nilai pengembalian dan memeriksa kode kesalahan Anda. Ini masih merupakan konvensi yang bagus ketika Anda berada di C.
OneOf
Anda menggunakan konvensi lain lagi. Tidak masalah selama Anda mengatur konvensi dan tidak hanya melawan yang lain. Sangat membingungkan memiliki dua konvensi kesalahan dalam basis kode yang sama. Jika entah bagaimana Anda sudah menyingkirkan semua kode yang menggunakan konvensi lain, silakan.
Saya suka konvensi itu sendiri. Salah satu penjelasan terbaiknya saya temukan di sini * :
Tetapi sebanyak yang saya suka saya masih tidak akan mencampurnya dengan konvensi lain. Pilih satu dan pertahankan. 1
1: Maksud saya jangan membuat saya memikirkan lebih dari satu kebaktian pada saat yang bersamaan.
Pikiran selanjutnya:
Dari diskusi ini apa yang saya ambil saat ini adalah sebagai berikut:
- Jika Anda mengharapkan penelepon segera menangkap dan menangani pengecualian sebagian besar waktu dan melanjutkan pekerjaannya, mungkin melalui jalur lain, itu mungkin harus menjadi bagian dari tipe pengembalian. Opsional atau OneOf dapat bermanfaat di sini.
- Jika Anda mengharapkan penelepon segera tidak menangkap pengecualian sebagian besar waktu, melemparkan pengecualian, untuk menyimpan kekonyolan melewatkannya secara manual ke tumpukan.
- Jika Anda tidak yakin apa yang akan dilakukan pemanggil langsung, mungkin berikan keduanya, seperti Parse dan TryParse.
Ini benar-benar tidak sesederhana ini. Salah satu hal mendasar yang perlu Anda pahami adalah apa itu nol.
Berapa hari tersisa di bulan Mei? 0 (karena ini bukan bulan Mei. Sudah bulan Juni).
Pengecualian adalah cara untuk menolak asumsi tetapi bukan satu-satunya cara. Jika Anda menggunakan pengecualian untuk menolak asumsi Anda meninggalkan jalan bahagia. Tetapi jika Anda memilih nilai untuk mengirim jalan bahagia yang menandakan bahwa hal-hal tidak sesederhana seperti yang diasumsikan maka Anda bisa tetap berada di jalur itu selama itu bisa berurusan dengan nilai-nilai itu. Terkadang 0 sudah digunakan untuk mengartikan sesuatu sehingga Anda harus menemukan nilai lain untuk memetakan asumsi penolakan Anda. Anda mungkin mengenali ide ini dari penggunaannya dalam aljabar lama yang bagus . Monads dapat membantu dengan itu tetapi tidak harus selalu menjadi monad.
Misalnya 2 :
IList<int> ParseAllTheInts(String s) { ... }
Bisakah Anda memikirkan alasan bagus mengapa ini harus dirancang sedemikian rupa sehingga dengan sengaja melemparkan apa pun? Tebak apa yang Anda dapatkan ketika int tidak bisa diuraikan? Aku bahkan tidak perlu memberitahumu.
Itu pertanda nama baik. Maaf tapi TryParse bukan ide saya tentang nama baik.
Kita sering menghindari melempar perkecualian untuk tidak mendapatkan apa-apa ketika jawabannya bisa lebih dari satu hal pada saat yang sama tetapi untuk beberapa alasan jika jawabannya adalah satu hal atau tidak sama sekali, kita terobsesi untuk bersikeras bahwa itu memberi kita satu hal atau lempar:
IList<Point> Intersection(Line a, Line b) { ... }
Apakah garis paralel benar-benar perlu menyebabkan pengecualian di sini? Benarkah seburuk itu jika daftar ini tidak akan mengandung lebih dari satu poin?
Mungkin secara semantik Anda tidak bisa menerimanya. Jika demikian, sangat disayangkan. Tapi Mungkin Monads, yang tidak memiliki ukuran sewenang-wenang seperti List
itu, akan membuat Anda merasa lebih baik tentang hal itu.
Maybe<Point> Intersection(Line a, Line b) { ... }
The Monads adalah koleksi tujuan khusus kecil yang dimaksudkan untuk digunakan dengan cara tertentu yang menghindari perlu mengujinya. Kita seharusnya menemukan cara untuk berurusan dengan mereka terlepas dari apa yang dikandungnya. Dengan begitu jalan yang bahagia tetap sederhana. Jika Anda membuka dan menguji setiap Monad yang Anda sentuh, Anda salah menggunakannya.
Saya tahu, ini aneh. Tetapi ini adalah alat baru (bagi kami). Jadi berikan waktu. Palu lebih masuk akal ketika Anda berhenti menggunakannya pada sekrup.
Jika Anda akan memanjakan saya, saya ingin membahas komentar ini:
Kenapa tidak ada jawaban yang menjelaskan bahwa monad bukan kode kesalahan, dan OneOf juga tidak? Mereka pada dasarnya berbeda, dan pertanyaannya tampaknya didasarkan pada kesalahpahaman. (Meskipun dalam bentuk yang dimodifikasi itu masih pertanyaan yang valid.) - Konrad Rudolph 4 Jun 18 pada 14:08
Ini mutlak benar. Monads jauh lebih dekat dengan koleksi daripada pengecualian, bendera, atau kode kesalahan. Mereka membuat wadah yang bagus untuk hal-hal seperti itu ketika digunakan dengan bijak.
Result
;-)