การตรวจสอบ vs ไม่ได้ตรวจสอบ vs ไม่มีข้อยกเว้น ... แนวปฏิบัติที่ดีที่สุดของความเชื่อที่ตรงกันข้าม


10

มีข้อกำหนดมากมายที่จำเป็นสำหรับระบบในการถ่ายทอดและจัดการข้อยกเว้นอย่างเหมาะสม นอกจากนี้ยังมีตัวเลือกมากมายสำหรับภาษาที่เลือกจากการนำแนวคิดไปใช้

ข้อกำหนดสำหรับการยกเว้น (ตามลำดับโดยเฉพาะ):

  1. เอกสาร : ภาษาควรมีค่าเฉลี่ยในการยกเว้นเอกสารที่ API สามารถใช้งานได้ โดยทั่วไปแล้วสื่อเอกสารนี้ควรใช้กับเครื่องเพื่อให้คอมไพเลอร์และ IDE สามารถให้การสนับสนุนแก่โปรแกรมเมอร์

  2. ส่งสถานการณ์พิเศษ : อันนี้ชัดเจนเพื่อให้ฟังก์ชั่นในการถ่ายทอดสถานการณ์ที่ป้องกันไม่ให้การทำงานที่เรียกว่าดำเนินการตามที่คาดไว้ ในความคิดของฉันมีสามประเภทใหญ่ของสถานการณ์เช่นนี้:

    2.1 ข้อบกพร่องในรหัสที่ทำให้ข้อมูลบางอย่างไม่ถูกต้อง

    2.2 ปัญหาในการกำหนดค่าหรือทรัพยากรภายนอกอื่น ๆ

    2.3 ทรัพยากรที่ไม่น่าเชื่อถืออย่างแท้จริง (เครือข่ายระบบไฟล์ฐานข้อมูลผู้ใช้ปลายทางเป็นต้น) สิ่งเหล่านี้เป็นเรื่องเล็ก ๆ น้อย ๆ เนื่องจากลักษณะที่ไม่น่าเชื่อถือของพวกเขาควรให้เราคาดหวังความล้มเหลวเป็นระยะ ๆ ในกรณีนี้สถานการณ์เหล่านี้จะถือว่ายอดเยี่ยมหรือไม่

  3. ให้ข้อมูลที่เพียงพอสำหรับรหัสในการจัดการ : ข้อยกเว้นควรให้ข้อมูลที่เพียงพอแก่ผู้ใช้เพื่อให้สามารถตอบสนองและจัดการกับสถานการณ์ได้ ข้อมูลควรเพียงพอเช่นกันเมื่อบันทึกข้อยกเว้นนี้จะให้บริบทที่เพียงพอแก่โปรแกรมเมอร์เพื่อระบุและแยกคำแถลงที่กระทำผิดและจัดหาวิธีแก้ปัญหา

  4. ให้ความมั่นใจแก่โปรแกรมเมอร์เกี่ยวกับสถานะปัจจุบันของสถานะการเรียกใช้รหัสของเขา : ความสามารถในการจัดการข้อยกเว้นของระบบซอฟต์แวร์ควรมีเพียงพอที่จะให้การป้องกันที่จำเป็นขณะที่อยู่ห่างจากโปรแกรมเมอร์เพื่อให้เขาสามารถจดจ่อกับงานที่ มือ.

เพื่อให้ครอบคลุมถึงวิธีการต่อไปนี้ถูกนำไปใช้ในภาษาต่างๆ:

  1. ข้อยกเว้นที่ตรวจสอบ มีวิธีที่ดีในการจัดทำเอกสารข้อยกเว้นและในทางทฤษฎีเมื่อดำเนินการอย่างถูกต้องควรให้ความมั่นใจอย่างเพียงพอว่าทุกอย่างดี อย่างไรก็ตามค่าใช้จ่ายดังกล่าวทำให้หลายคนรู้สึกว่ามีประสิทธิผลมากกว่าที่จะหลีกเลี่ยงโดยการกลืนข้อยกเว้นหรือโยนทิ้งเป็นข้อยกเว้นที่ไม่ได้ตรวจสอบอีกครั้ง เมื่อใช้ข้อยกเว้นที่ตรวจสอบอย่างไม่เหมาะสมก็จะมีประโยชน์ นอกจากนี้การตรวจสอบข้อยกเว้นทำให้ยากในการสร้าง API ที่เสถียรในเวลา การใช้งานระบบทั่วไปภายในโดเมนที่เฉพาะเจาะจงจะทำให้เกิดสถานการณ์ที่ไม่ธรรมดาซึ่งยากต่อการบำรุงรักษาโดยใช้ข้อยกเว้นที่ตรวจสอบ แต่เพียงผู้เดียว

  2. ข้อยกเว้นที่ไม่ได้ตรวจสอบ - มีความหลากหลายมากกว่าข้อยกเว้นที่ตรวจสอบพวกเขาไม่สามารถจัดทำเอกสารสถานการณ์พิเศษที่เป็นไปได้ของการใช้งานที่ระบุ พวกเขาพึ่งพาเอกสารแบบเฉพาะกิจถ้าทั้งหมด สิ่งนี้จะสร้างสถานการณ์ที่ธรรมชาติที่ไม่น่าเชื่อถือของสื่อถูกหลอกลวงโดย API ที่ให้ความน่าเชื่อถือ นอกจากนี้เมื่อโยนข้อยกเว้นเหล่านี้หลุดความหมายของพวกเขาในขณะที่พวกเขาย้ายกลับขึ้นไปในชั้นนามธรรม เนื่องจากพวกเขาได้รับการบันทึกไม่ดีโปรแกรมเมอร์จึงไม่สามารถกำหนดเป้าหมายได้อย่างเฉพาะเจาะจงและมักจะต้องใช้เครือข่ายที่กว้างกว่าที่จำเป็นเพื่อให้แน่ใจว่าระบบรองหากพวกเขาล้มเหลวอย่าทำให้ระบบทั้งหมดล้มเหลว ซึ่งนำเรากลับไปที่ข้อยกเว้นการตรวจสอบปัญหาการกลืนที่ให้ไว้

  3. Multistate return types ที่ นี่เป็นการพึ่งพาชุด disjoint, tuple หรือแนวคิดที่คล้ายกันอื่น ๆ เพื่อส่งคืนผลลัพธ์ที่คาดหวังหรือวัตถุที่แสดงถึงข้อยกเว้น ที่นี่ไม่มีการคลายสแต็กไม่มีการตัดผ่านโค้ดทุกอย่างดำเนินการตามปกติ แต่ค่าส่งคืนจะต้องได้รับการตรวจสอบความผิดพลาดก่อนดำเนินการต่อ ฉันยังไม่ได้ทำงานกับเรื่องนี้เลยดังนั้นจึงไม่สามารถแสดงความคิดเห็นจากประสบการณ์ที่ฉันรับทราบว่ามันช่วยแก้ปัญหาข้อยกเว้นบางอย่างผ่านการไหลปกติ แต่ก็ยังจะประสบปัญหาเดียวกันมากกับข้อยกเว้นที่ตรวจสอบว่าน่าเบื่อและตลอดเวลา

ดังนั้นคำถามคือ:

ประสบการณ์ของคุณในเรื่องนี้คืออะไรและตามที่คุณเป็นผู้สมัครที่ดีที่สุดในการสร้างระบบการจัดการข้อยกเว้นที่ดีสำหรับภาษาที่จะมี?


แก้ไข: ไม่กี่นาทีหลังจากเขียนคำถามนี้ฉันเจอโพสต์นี้น่ากลัว!


2
"มันจะประสบปัญหาเดียวกับข้อยกเว้นที่ถูกตรวจสอบว่าน่าเบื่อและต่อเนื่องตลอดเวลาในหน้าของคุณ": ไม่จริง: ด้วยการสนับสนุนด้านภาษาที่เหมาะสมคุณต้องตั้งโปรแกรม "เส้นทางสู่ความสำเร็จ" ด้วยเครื่องมือภาษาพื้นฐานที่ดูแลการเผยแพร่ ข้อผิดพลาด
Giorgio

"ภาษาควรมีค่าเฉลี่ยในการยกเว้นเอกสารที่ API สามารถใช้งานได้" - weeeel ใน C ++ "เรา" ได้เรียนรู้ว่าสิ่งนี้ไม่ได้ผลจริงๆ ทั้งหมดที่คุณสามารถจริงๆประโยชน์ทำคือการรัฐว่า API สามารถโยนใด ๆยกเว้น (นั่นเป็นการตัดเรื่องสั้นที่สั้นจริงๆ แต่ฉันคิดว่าการดูnoexceptเรื่องราวใน C ++ สามารถให้ข้อมูลเชิงลึกที่ดีมากสำหรับ EH ใน C # และ Java เช่นกัน)
Martin Ba

คำตอบ:


10

ในช่วงแรก ๆ ของ C ++ เราค้นพบว่าหากไม่มีการเขียนโปรแกรมทั่วไปภาษาที่พิมพ์อย่างมากนั้นไม่ได้ตั้งใจอย่างยิ่ง นอกจากนี้เรายังค้นพบว่าข้อยกเว้นที่ตรวจสอบแล้วและการเขียนโปรแกรมทั่วไปทำงานได้ไม่ดีและการยกเว้นที่ตรวจสอบแล้วนั้นถูกทิ้งร้าง

ประเภทการคืนหลายค่านั้นยอดเยี่ยม แต่ไม่มีการทดแทนสำหรับข้อยกเว้น ไม่มีข้อยกเว้นรหัสเต็มไปด้วยเสียงการตรวจสอบข้อผิดพลาด

ปัญหาอื่น ๆ ที่มีการตรวจสอบข้อยกเว้นคือการเปลี่ยนแปลงในข้อยกเว้นที่เกิดจากฟังก์ชั่นระดับต่ำจะบังคับให้มีการเรียงซ้อนของการเปลี่ยนแปลงในผู้โทรทั้งหมดและผู้โทรและอื่น ๆ วิธีเดียวที่จะป้องกันปัญหานี้คือสำหรับรหัสแต่ละระดับในการตรวจจับข้อยกเว้นใด ๆ ที่ถูกโยนโดยระดับที่ต่ำกว่าและล้อมรอบด้วยข้อยกเว้นใหม่ อีกครั้งคุณท้ายด้วยรหัสที่มีเสียงดังมาก


2
ยาสามัญจะช่วยแก้ไขข้อผิดพลาดทั้งชั้นซึ่งส่วนใหญ่เกิดจากข้อ จำกัด ของการสนับสนุนภาษาของกระบวนทัศน์ OO แต่ถึงกระนั้นทางเลือกที่ดูเหมือนว่าจะมีรหัสที่ส่วนใหญ่ทำการตรวจสอบข้อผิดพลาดหรือที่ทำงานหวังว่าไม่มีอะไรผิดพลาด ไม่ว่าคุณจะมีสถานการณ์ที่ยอดเยี่ยมอย่างต่อเนื่องในหน้าของคุณหรืออาศัยอยู่ในดินแดนในฝันของกระต่ายสีขาวปุยที่เปลี่ยนไปอย่างน่าเกลียดเมื่อคุณวางหมาป่าตัวใหญ่ไว้ตรงกลาง!
Newtopian

3
+1 สำหรับปัญหาการเรียงซ้อน ระบบ / สถาปัตยกรรมใด ๆ ที่ทำให้การเปลี่ยนแปลงยากนำไปสู่ระบบการปะแก้ของลิงและความยุ่งเหยิงเท่านั้นไม่ว่าผู้แต่งคิดว่าออกแบบมาเป็นอย่างไร
Matthieu M.

2
@ Newtopian: เทมเพลตทำสิ่งที่ไม่สามารถทำได้ในการวางแนววัตถุที่เข้มงวดเช่นจัดเตรียมความปลอดภัยประเภทคงที่สำหรับภาชนะบรรจุทั่วไป
David Thornley

2
ฉันต้องการดูระบบการยกเว้นที่มีแนวคิดของ "การตรวจสอบข้อยกเว้น" แต่แตกต่างกันมากจาก Java ของ Checked-ness ไม่ควรเป็นคุณลักษณะของประเภทข้อยกเว้นแต่ควรโยนไซต์ไซต์ที่จับและอินสแตนซ์ข้อยกเว้น หากมีการโฆษณาวิธีการโยนข้อยกเว้นที่ตรวจสอบแล้วว่าควรมีผลสองประการ: (1) ฟังก์ชั่นควรจัดการ "การโยน" ของข้อยกเว้นที่ตรวจสอบโดยทำสิ่งพิเศษกลับ (เช่นการตั้งค่าธงพกพา ฯลฯ ขึ้นอยู่กับ แพลตฟอร์มที่แน่นอน) ซึ่งจะต้องมีการเตรียมรหัสสำหรับการโทร
supercat

7
"ไม่มีข้อยกเว้นรหัสเต็มไปด้วยเสียงการตรวจสอบข้อผิดพลาด": ฉันไม่แน่ใจเกี่ยวกับสิ่งนี้: ใน Haskell คุณสามารถใช้ monads สำหรับสิ่งนี้และเสียงการตรวจสอบข้อผิดพลาดทั้งหมดหายไป เสียงรบกวนที่แนะนำโดย "ประเภทการส่งคืนแบบหลายขั้นตอน" เป็นข้อ จำกัด ของภาษาการเขียนโปรแกรมมากกว่าวิธีการแก้ปัญหาในตัวเอง
Giorgio

9

เป็นเวลานานภาษา OO การใช้ข้อยกเว้นเป็นมาตรฐาน de-facto สำหรับการสื่อสารข้อผิดพลาด แต่ภาษาโปรแกรมที่ใช้งานได้นั้นมีความเป็นไปได้ของวิธีการที่แตกต่างกันเช่นการใช้ monads (ซึ่งฉันไม่ได้ใช้) หรือ "การเขียนโปรแกรมเชิงรถไฟที่มีน้ำหนักเบา" ตามที่อธิบายโดย Scott Wlaschin

มันเป็นความแตกต่างของประเภทผลลัพธ์แบบหลายระดับ

  • ฟังก์ชันส่งคืนทั้งความสำเร็จหรือข้อผิดพลาด มันไม่สามารถส่งคืนทั้งสอง (เช่นเดียวกับกรณีที่มี tuple)
  • ข้อผิดพลาดที่เป็นไปได้ทั้งหมดได้รับการบันทึกไว้โดยสังเขป (อย่างน้อยใน F # ที่มีประเภทผลลัพธ์เป็นสหภาพที่จำแนกได้)
  • ผู้เรียกไม่สามารถใช้ผลลัพธ์โดยไม่คำนึงถึงว่าผลลัพธ์นั้นประสบความสำเร็จหรือล้มเหลว

ประเภทผลลัพธ์สามารถประกาศได้เช่นนี้

type Result<'TSuccess,'TFail> =
| Success of 'TSuccess
| Fail of 'TFail

ดังนั้นผลลัพธ์ของฟังก์ชันที่คืนค่าชนิดนี้อาจSuccessเป็นFailชนิดหรือชนิด มันไม่สามารถเป็นได้ทั้งคู่

ในภาษาการเขียนโปรแกรมเชิงความสำคัญเพิ่มเติมรูปแบบชนิดนี้อาจต้องใช้รหัสจำนวนมากในเว็บไซต์ของผู้โทร แต่ฟังก์ชั่นการเขียนโปรแกรมช่วยให้คุณสร้างฟังก์ชั่นที่มีผลผูกพันหรือผู้ประกอบการที่จะรวมกันหลายฟังก์ชั่นเพื่อตรวจสอบข้อผิดพลาดไม่ใช้รหัสครึ่ง ตัวอย่างเช่น:

// Create an updateUser function that takes an id, and new state
// as input, and updates an existing user.
let updateUser id input =
    validateInput input
    >>= loadUser id
    >>= updateUser input
    >>= saveUser id
    >>= notifyAboutUserUpdated

updateUserฟังก์ชั่นการโทรแต่ละฟังก์ชั่นเหล่านี้ในการสืบทอดและแต่ละของพวกเขาสามารถล้มเหลว หากพวกเขาทั้งหมดประสบความสำเร็จผลลัพธ์ของฟังก์ชั่นที่เรียกว่าสุดท้ายจะถูกส่งกลับ หากฟังก์ชันใดฟังก์ชันหนึ่งล้มเหลวผลลัพธ์ของฟังก์ชันนั้นจะเป็นผลลัพธ์ของupdateUserฟังก์ชันโดยรวม ทั้งหมดนี้จัดการโดยตัวดำเนินการ >> = ที่กำหนดเอง

ในตัวอย่างข้างต้นประเภทข้อผิดพลาดอาจเป็น

type UserValidationErrorType =
| InvalidEmail of string
| MissingFirstName of string
... etc

type DbErrorType =
| RecordNotFound of int
| ConcurrencyError of int

type UpdateUserErrorType =
| InvalidInput of UserValidationErrorType
| DbError of DbErrorType

หากผู้เรียกupdateUserไม่จัดการข้อผิดพลาดที่เป็นไปได้ทั้งหมดจากฟังก์ชันคอมไพเลอร์จะออกคำเตือน ดังนั้นคุณมีเอกสารทุกอย่าง

ใน Haskell มีdoสัญกรณ์ที่สามารถทำให้โค้ดสะอาดยิ่งขึ้น


2
คำตอบและการอ้างอิงที่ดีมาก (การเขียนโปรแกรมเชิงรถไฟ) +1 คุณอาจต้องการพูดถึงdoสัญกรณ์ของ Haskell ซึ่งทำให้รหัสที่ได้นั้นสะอาดยิ่งขึ้น
Giorgio

1
@Giorgio - ตอนนี้ฉันได้ แต่ฉันไม่ได้ทำงานกับ Haskell เพียง F # ดังนั้นฉันไม่สามารถเขียนมากเกี่ยวกับมัน แต่คุณสามารถเพิ่มคำตอบได้หากคุณต้องการ
Pete

ขอบคุณฉันเขียนตัวอย่างเล็ก ๆ แต่เนื่องจากมันไม่เล็กพอที่จะเพิ่มในคำตอบของคุณฉันจึงเขียนคำตอบที่สมบูรณ์ (พร้อมข้อมูลพื้นหลังเพิ่มเติมบางส่วน)
Giorgio

2
Railway Oriented Programmingเป็นพฤติกรรมเอกว่า
Daenyth

5

ฉันพบคำตอบของ Peteที่ดีมากและฉันต้องการเพิ่มการพิจารณาและตัวอย่างหนึ่ง การสนทนาที่น่าสนใจมากเกี่ยวกับการใช้ข้อยกเว้นกับการส่งคืนค่าความผิดพลาดพิเศษสามารถพบได้ในการเขียนโปรแกรมใน Standard ML โดย Robert Harperที่ส่วนท้ายของมาตรา 29.3, หน้า 243, 244

ปัญหาคือจะใช้ฟังก์ชั่นบางส่วนกลับค่าของบางชนิดf tทางออกหนึ่งคือการมีฟังก์ชั่นมีประเภท

f : ... -> t

และโยนข้อยกเว้นเมื่อไม่มีผลลัพธ์ที่เป็นไปได้ ทางออกที่สองคือการใช้ฟังก์ชั่นที่มีประเภท

f : ... -> t option

และกลับSOME vไปสู่ความสำเร็จและNONEล้มเหลว

นี่คือข้อความจากหนังสือที่มีการปรับแต่งเล็ก ๆ ด้วยตัวเองเพื่อทำให้ข้อความกว้างขึ้น (หนังสือเล่มนี้อ้างถึงตัวอย่างเฉพาะ) การแก้ไขข้อความที่เขียนในตัวเอียง

ข้อเสียระหว่างทั้งสองโซลูชั่นคืออะไร

  1. การแก้ปัญหาตามประเภทของตัวเลือกทำให้ชัดเจนในประเภทของฟังก์ชั่นfความเป็นไปได้ของความล้มเหลว สิ่งนี้บังคับให้โปรแกรมเมอร์ทำการทดสอบความล้มเหลวอย่างชัดเจนโดยใช้การวิเคราะห์เคสจากผลลัพธ์ของการโทร ตัวตรวจสอบชนิดจะตรวจสอบให้แน่ใจว่าไม่สามารถใช้งานได้ตาม t optionที่tคาดไว้ วิธีการแก้ปัญหาตามข้อยกเว้นไม่ได้ระบุอย่างชัดเจนถึงความล้มเหลวในประเภทของมัน อย่างไรก็ตามโปรแกรมเมอร์จะถูกบังคับให้จัดการกับความล้มเหลวมิฉะนั้นจะเกิดข้อผิดพลาดที่ไม่ได้รับการตรวจสอบในเวลาทำงานแทนที่จะรวบรวมเวลา
  2. วิธีการแก้ปัญหาตามประเภทตัวเลือกต้องมีการวิเคราะห์กรณีที่ชัดเจนเกี่ยวกับผลของการโทรแต่ละครั้ง หากผลลัพธ์ส่วนใหญ่ประสบความสำเร็จการตรวจสอบซ้ำซ้อนจึงมีค่าใช้จ่ายสูงเกินไป วิธีการแก้ปัญหาตามข้อยกเว้นนั้นไม่มีค่าใช้จ่ายนี้: มันเอนเอียงไปทางกรณี "ปกติ" ของการส่งคืนtมากกว่ากรณี "ล้มเหลว" ที่ไม่ส่งคืนผลลัพธ์เลย การดำเนินการตามข้อยกเว้นทำให้มั่นใจได้ว่าการใช้ตัวจัดการนั้นมีประสิทธิภาพมากกว่าการวิเคราะห์กรณีอย่างชัดเจนในกรณีที่ความล้มเหลวนั้นเกิดขึ้นน้อยมากเมื่อเทียบกับความสำเร็จ

[ตัด] โดยทั่วไปหากประสิทธิภาพเป็นสิ่งสำคัญยิ่งเรามักจะชอบข้อยกเว้นหากความล้มเหลวเป็นสิ่งที่หายากและต้องการตัวเลือกหากความล้มเหลวเป็นเรื่องปกติ หากในทางกลับกันการตรวจสอบแบบคงที่เป็นสิ่งสำคัญยิ่งก็เป็นข้อได้เปรียบในการใช้ตัวเลือกเนื่องจากตัวตรวจสอบชนิดจะบังคับใช้ข้อกำหนดที่โปรแกรมเมอร์ตรวจสอบความล้มเหลวแทนที่จะมีข้อผิดพลาดเกิดขึ้นเฉพาะในเวลาทำงาน

สิ่งนี้จะเกี่ยวข้องกับตัวเลือกระหว่างข้อยกเว้นและประเภทการส่งคืนตัวเลือก

เกี่ยวกับแนวคิดที่แสดงถึงข้อผิดพลาดในประเภทส่งคืนจะนำไปสู่การตรวจสอบข้อผิดพลาดแพร่กระจายทั่วรหัส: ไม่จำเป็นต้องเป็นกรณีนี้ นี่คือตัวอย่างเล็ก ๆ ใน Haskell ที่แสดงสิ่งนี้

สมมติว่าเราต้องการแยกตัวเลขสองตัวแล้วหารแรกด้วยสอง ดังนั้นอาจมีข้อผิดพลาดขณะแยกแต่ละหมายเลขหรือเมื่อหาร (หารด้วยศูนย์) ดังนั้นเราต้องตรวจสอบข้อผิดพลาดหลังจากแต่ละขั้นตอน

import Text.Read

parseInt :: String -> Maybe Int
parseInt s = readMaybe s :: Maybe Int

safeDiv :: Int -> Int -> Maybe Int
safeDiv n d = if d /= 0 then Just (n `div` d) else Nothing

toString :: Maybe Int -> String
toString (Just i) = show i
toString Nothing  = "error"

main = do
         -- Get two lines from the terminal.
         nStr <- getLine
         dStr <- getLine

         -- Parse each string and divide.
         let r = do n <- parseInt nStr
                    d <- parseInt dStr
                    safeDiv n d

         -- Print the result.
         putStrLn $ toString r

การแยกและการแบ่งจะดำเนินการในlet ...บล็อก โปรดทราบว่าด้วยการใช้Maybemonad และdoสัญกรณ์จะมีการระบุเฉพาะเส้นทางแห่งความสำเร็จเท่านั้น: ซีแมนทิกส์ของMaybemonad จะถ่ายทอดค่าความผิดพลาดโดยปริยาย ( Nothing) ไม่มีค่าใช้จ่ายสำหรับโปรแกรมเมอร์


2
ฉันคิดว่าในกรณีเช่นนี้ที่คุณต้องการพิมพ์ข้อความแสดงข้อผิดพลาดที่มีประโยชน์บางEitherประเภทจะเหมาะสมกว่า คุณจะทำอย่างไรถ้าคุณมาNothingที่นี่? คุณเพิ่งได้รับข้อความ "ข้อผิดพลาด" ไม่เป็นประโยชน์สำหรับการแก้ไขข้อบกพร่อง
sara

1

ฉันเป็นแฟนตัวยงของ Checked Exceptions และฉันต้องการแบ่งปันกฎทั่วไปของฉันว่าควรใช้เมื่อใด

ฉันได้ข้อสรุปว่ามีข้อผิดพลาด 2 ประเภทที่รหัสของฉันต้องจัดการ มีข้อผิดพลาดที่สามารถทดสอบได้ก่อนที่รหัสจะทำงานและมีข้อผิดพลาดที่ไม่สามารถทดสอบได้ก่อนที่รหัสจะทำงาน ตัวอย่างง่ายๆสำหรับข้อผิดพลาดที่สามารถทดสอบได้ก่อนที่รหัสจะถูกดำเนินการใน NullPointerException

//... bad code below.  the runnable variable
// tries to call the run() method before the variable
// is instantiated.  Running the code below will cause
// a NullPointerException.
Runnable runnable = null;
runnable.run();

การทดสอบแบบง่าย ๆ สามารถหลีกเลี่ยงข้อผิดพลาดเช่น ...

Runnable runnable = null;
...
if (runnable != null)
{   runnable.run(); }

มีหลายครั้งในการคำนวณที่คุณสามารถรันการทดสอบ 1 ครั้งขึ้นไปก่อนที่จะรันโค้ดเพื่อให้แน่ใจว่าคุณปลอดภัยและคุณจะได้รับการยกเว้น ตัวอย่างเช่นคุณสามารถทดสอบระบบไฟล์เพื่อให้แน่ใจว่ามีพื้นที่ว่างในฮาร์ดดิสก์เพียงพอก่อนที่จะเขียนข้อมูลลงในไดรฟ์ ในระบบปฏิบัติการหลายตัวประมวลผลเช่นที่ใช้ในปัจจุบันกระบวนการของคุณสามารถทดสอบพื้นที่ดิสก์และระบบไฟล์จะคืนค่าที่บอกว่ามีเนื้อที่เพียงพอจากนั้นการสลับบริบทไปยังกระบวนการอื่นสามารถเขียนไบต์ที่เหลือที่พร้อมใช้งานสำหรับปฏิบัติการ ระบบ. เมื่อบริบทระบบปฏิบัติการสลับกลับไปยังกระบวนการทำงานของคุณซึ่งคุณเขียนเนื้อหาลงในดิสก์ข้อยกเว้นจะเกิดขึ้นเพียงเพราะมีพื้นที่ดิสก์ไม่เพียงพอในระบบไฟล์

ฉันพิจารณาสถานการณ์ข้างต้นเป็นกรณีที่สมบูรณ์แบบสำหรับข้อยกเว้นที่ตรวจสอบ มันเป็นข้อยกเว้นในโค้ดที่บังคับให้คุณต้องจัดการกับสิ่งที่ไม่ดีแม้ว่ารหัสของคุณจะสามารถเขียนได้อย่างสมบูรณ์แบบ หากคุณเลือกที่จะทำสิ่งที่ไม่ดีเช่น 'กลืนข้อยกเว้น' คุณเป็นโปรแกรมเมอร์ที่ไม่ดี โดยวิธีการที่ฉันได้พบกรณีที่มีเหตุผลที่จะกลืนข้อยกเว้น แต่โปรดแสดงความคิดเห็นในรหัสว่าทำไมข้อยกเว้นถูกกลืน กลไกการจัดการข้อยกเว้นไม่ใช่การตำหนิ ฉันมักพูดเล่น ๆ ว่าฉันต้องการให้เครื่องกระตุ้นหัวใจของฉันเขียนด้วยภาษาที่มีข้อยกเว้นที่ตรวจสอบแล้ว

มีบางครั้งที่มันยากที่จะตัดสินใจว่ารหัสทดสอบได้หรือไม่ ตัวอย่างเช่นถ้าคุณกำลังเขียนล่ามและ SyntaxException จะถูกโยนทิ้งเมื่อโค้ดไม่สามารถดำเนินการด้วยเหตุผลทางไวยากรณ์บางอย่างไวยากรณ์ควรจะเป็นไวยากรณ์ที่ตรวจสอบหรือ (ใน Java) RuntimeException หรือไม่ ฉันจะตอบว่าล่ามตรวจสอบไวยากรณ์ของรหัสก่อนที่รหัสจะถูกดำเนินการแล้วข้อยกเว้นควรเป็น RuntimeException หากล่ามเรียกใช้รหัส 'ร้อน' และเพียงพบข้อผิดพลาดของไวยากรณ์ฉันจะบอกว่าข้อยกเว้นควรเป็นข้อยกเว้นที่ตรวจสอบแล้ว

ฉันจะยอมรับว่าฉันไม่ได้มีความสุขเสมอที่จะต้องจับหรือโยนข้อยกเว้นที่ตรวจสอบเพราะมีเวลาที่ฉันไม่แน่ใจว่าจะทำอย่างไร การตรวจสอบข้อยกเว้นเป็นวิธีการบังคับให้โปรแกรมเมอร์ให้ระวังปัญหาที่อาจเกิดขึ้นได้ หนึ่งในเหตุผลที่ฉันโปรแกรมใน Java เป็นเพราะมันมีการตรวจสอบข้อยกเว้น


1
ฉันอยากจะเขียนเครื่องกระตุ้นหัวใจของฉันในภาษาที่ไม่มีข้อยกเว้นเลยและรหัสทุกบรรทัดจัดการข้อผิดพลาดผ่านรหัสส่งคืน เมื่อคุณโยนข้อยกเว้นคุณจะพูดว่า "มันผิดพลาดทั้งหมด" และวิธีเดียวที่ปลอดภัยในการดำเนินการต่อคือหยุดและเริ่มต้นใหม่ โปรแกรมที่จบลงอย่างง่ายดายในสถานะที่ไม่ถูกต้องไม่ใช่สิ่งที่คุณต้องการสำหรับซอฟต์แวร์ที่สำคัญ (และ Java ไม่อนุญาตให้ใช้ซอฟต์แวร์ที่สำคัญใน EULA อย่างชัดเจน)
gbjbaanb

การใช้ข้อยกเว้นและไม่ตรวจสอบพวกเขาเทียบกับการใช้รหัสส่งคืนและไม่ตรวจสอบพวกเขาในท้ายที่สุดมันทั้งหมดให้หัวใจหยุดเต้นเหมือนกัน
Newtopian

-1

ขณะนี้ฉันอยู่ในกลางโครงการ OOP / API ที่ค่อนข้างใหญ่และฉันใช้โครงร่างของข้อยกเว้นนี้ แต่ทุกอย่างขึ้นอยู่กับว่าคุณต้องการลึกแค่ไหนกับการจัดการข้อยกเว้นและสิ่งที่คล้ายกัน

ExpectedException
- AuthorisedException
- EmptySetException
- NoRemainingException
- NoRowsException
- NotFoundException
- ValidationException

UnexpectedException
- ConnectivityException
- EnvironmentException
- ProgrammerException
- SQLException

ตัวอย่าง

   $valid_types = array('mysql', 'oracle', 'sqlite');
       if (!in_array($type, $valid_types)) {
           throw new ecProgrammerException(
        'The database type specified, %1$s, is invalid. Must be one of: %2$s.',
    $type,
    join(', ', $valid_types)
    );
}

11
หากคาดว่ามีข้อยกเว้นแสดงว่าไม่ใช่ข้อยกเว้นจริงๆ "NoRowsException"? เสียงเหมือนการควบคุมการไหลมาหาฉันดังนั้นการใช้ข้อยกเว้นที่ไม่ดี
quentin-starin

1
@qes: การเพิ่มข้อยกเว้นทุกครั้งที่ฟังก์ชันไม่สามารถคำนวณค่าได้เช่น double Math.sqrt (double v) หรือ User findUser (long id) สิ่งนี้ช่วยให้ผู้โทรมีอิสระในการจับและจัดการข้อผิดพลาดในที่ที่สะดวกแทนที่จะตรวจสอบหลังการโทรแต่ละครั้ง
kevin cline

1
คาดหวัง = control flow = anti-pattern of exception ไม่ควรใช้ข้อยกเว้นสำหรับโฟลว์การควบคุม หากคาดว่าจะสร้างข้อผิดพลาดสำหรับอินพุตที่เฉพาะเจาะจงก็จะถูกส่งผ่านส่วนหนึ่งของค่าตอบแทน ดังนั้นเราจึงมีหรือNAN NULL
Eonil

1
@Eonil ... หรือตัวเลือก <T>
Maarten Bodewes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.