วิธีการหลีกเลี่ยงการโยนข้อยกเว้นที่น่ารำคาญ?


21

การอ่านบทความของ Eric Lippert เกี่ยวกับข้อยกเว้นเป็นการเปิดตาอย่างแน่นอนเกี่ยวกับวิธีที่ฉันควรเข้าหาข้อยกเว้นทั้งในฐานะผู้ผลิตและผู้บริโภค อย่างไรก็ตามฉันยังคงพยายามกำหนดแนวทางเกี่ยวกับวิธีหลีกเลี่ยงการโยนข้อยกเว้นที่น่ารำคาญ

โดยเฉพาะ:

  • สมมติว่าคุณมีวิธีการบันทึกที่สามารถล้มเหลวเพราะก) ใครบางคนอื่น ๆ มีการปรับเปลี่ยนการบันทึกก่อนที่คุณหรือข) ค่าที่คุณกำลังพยายามที่จะสร้างที่มีอยู่แล้ว เงื่อนไขเหล่านี้คาดว่าจะเกิดขึ้นและไม่ได้ยอดเยี่ยมดังนั้นแทนที่จะทิ้งข้อยกเว้นที่คุณตัดสินใจที่จะสร้าง TrySave ของเมธอดของคุณซึ่งจะส่งคืนบูลีนที่ระบุว่าการบันทึกสำเร็จหรือไม่ แต่ถ้ามันล้มเหลวผู้บริโภคจะรู้ได้อย่างไรว่าปัญหาคืออะไร หรือมันจะดีที่สุดที่จะกลับ enum ระบุผลชนิดของ Ok / RecordAlreadyModified / ValueAlreadyExists? ด้วยจำนวนเต็ม TryParse ปัญหานี้ไม่มีอยู่เนื่องจากมีเพียงเหตุผลเดียวที่วิธีการล้มเหลว
  • ตัวอย่างก่อนหน้านี้เป็นสถานการณ์ที่น่ารำคาญจริง ๆ หรือไม่? หรือจะโยนข้อยกเว้นในกรณีนี้เป็นวิธีที่ต้องการหรือไม่ ฉันรู้ว่ามันเป็นวิธีการที่ทำในห้องสมุดและกรอบงานส่วนใหญ่รวมถึง Entity framework
  • คุณจะตัดสินใจเมื่อสร้างรุ่นลองของวิธีการของคุณเทียบกับวิธีการทดสอบก่อนว่าวิธีการทำงานหรือไม่ ฉันกำลังปฏิบัติตามแนวทางเหล่านี้:
    • หากมีโอกาสเกิดสภาวะการแข่งขันให้สร้างรุ่นทดลอง สิ่งนี้จะป้องกันความต้องการของผู้บริโภคที่จะได้รับการยกเว้นจากภายนอก ตัวอย่างเช่นในวิธีการบันทึกอธิบายไว้ก่อนหน้านี้
    • หากวิธีการทดสอบเงื่อนไขค่อนข้างจะทำทุกอย่างที่วิธีการเดิมทำแล้วให้ลองสร้างเวอร์ชัน ตัวอย่างเช่นจำนวนเต็ม.TryParse ()
    • ในกรณีอื่น ๆ ให้สร้างวิธีการทดสอบเงื่อนไข

1
ตัวอย่างของการบันทึกที่อาจล้มเหลวไม่ใช่ข้อยกเว้นที่น่ารำคาญอย่างยิ่ง มันเป็นเรื่องธรรมดามากและอาจจะควรก็เป็นข้อยกเว้น
S.Lott

@ S.Lott: คุณหมายถึงอะไรกับมันค่อนข้างธรรมดา? สถานการณ์ตัวเองหรือโยนข้อยกเว้นในสถานการณ์นี้? อย่างไรก็ตามฉันเห็นด้วยกับคุณว่ามันไม่ปรากฏชัดว่านี่เป็นสถานการณ์ที่น่ารำคาญ ฉันจะอัปเดตคำถาม
Mike

"สถานการณ์ตัวเองหรือโยนข้อยกเว้นในสถานการณ์นี้" ทั้งคู่
S.Lott

คำตอบ:


24

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

คำถามที่ดี.

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

คำถามที่สองที่อยู่ในใจของฉันคือ: ข้อมูลที่คุณต้องการกลับไปที่ผู้ใช้สามารถดำเนินการได้หรือไม่? นั่นคือพวกเขากำลังจะตัดสินใจบนพื้นฐานของข้อมูลที่?

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

ผู้โทรสนใจทำไมการบันทึกล้มเหลว? พวกเขากำลังจะทำอะไรเกี่ยวกับเรื่องนี้นอกจากจะยอมแพ้หรือลองอีกครั้งหรือไม่?

สมมติว่าสมมติว่าผู้โทรกำลังจะดำเนินการที่แตกต่างกันจริง ๆ ขึ้นอยู่กับเหตุผลว่าทำไมการดำเนินการล้มเหลว

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

สมมติว่าเป็นเรื่องที่ถกเถียงกัน

คำถามที่สี่ที่อยู่ในใจคือ: มีวิธีที่จะตรวจสอบสถานการณ์ที่ไม่ดีล่วงหน้าได้อย่างน่าเชื่อถือหรือไม่?

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

คำถามที่ห้าที่นึกถึงคือมีวิธีการออกแบบ API เพื่อให้สามารถป้องกันสถานการณ์ที่เลวร้ายได้หรือไม่?

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


ทุกจุดที่ดีมากขอบคุณ ต่อไปนี้เป็นคำถามเชิงโวหารที่อาจสรุปบทความของฉัน: หากคุณต้องการออกแบบ File.Open () ใหม่คุณจะสร้าง File.TryOpen () แทนหรือไม่ คุณจะสื่อสารผู้บริโภคถึงสาเหตุของความล้มเหลวได้อย่างไร? หรือกำลังขว้างข้อยกเว้นภายนอกที่ดีที่สุดที่นี่จริง ๆ ประนีประนอม?
Mike

10
@ ไมค์: ระบบไฟล์เป็นตัวอย่างที่ดีของการใช้ข้อยกเว้นจากภายนอก พวกเขาล้มเหลวไม่ค่อยบ่อยนัก พวกเขาล้มเหลวอย่างไม่อาจคาดการณ์ได้และด้วยเหตุผลทั้งหมดนอกการควบคุมของผู้โทร (ไม่มี "ล็อค" คุณสามารถใช้ที่ทำให้สายเคเบิลอีเธอร์เน็ตเสียบอยู่) และความล้มเหลวนั้นมีความหลากหลายและสามารถใช้งานได้ (นั่นคือความล้มเหลวเพราะไฟล์ ไม่พบไฟล์ vs ไม่พบ แต่คุณไม่มีสิทธิ์เข้าถึงเพื่อเขียนสามารถดำเนินการได้ในรูปแบบที่ต่างกัน) สิ่งเหล่านี้ล้วนเป็นเหตุผลที่แสดงถึงความล้มเหลวเป็นข้อยกเว้น
Eric Lippert

ฉันพิจารณาคำถามที่จะตอบตอนนี้ แต่ฉันแค่อยากรู้อยากเห็น;) หากวิธีการล้มเหลวด้วยเหตุผลสองข้อหรือมากกว่านั้นความล้มเหลวนั้นสามารถดำเนินการได้ความล้มเหลวนั้นเป็นแบบไม่ยอมรับ คุณจะทำอย่างไร
Mike

@ ไมค์ฉันพูดกับเอริคไม่ได้ แต่นั่นดูเหมือนว่าเป็นที่ที่ดีสำหรับรหัสผิดพลาด อาจส่งคืนสมาชิกของ enum บางที
Matthew อ่าน

1

ในตัวอย่างของคุณหากสถานการณ์ ValueAlreadyExists สามารถตรวจสอบได้ง่ายควรตรวจสอบและอาจมีข้อยกเว้นเกิดขึ้นก่อนที่จะลองบันทึกฉันไม่คิดว่าควรจะลองในสถานการณ์นี้ สภาพการแข่งขันนั้นยากที่จะตรวจสอบล่วงหน้าดังนั้นการปิดการบันทึกในการลองในกรณีนี้อาจเป็นความคิดที่ดีมาก

โดยทั่วไปหากมีเงื่อนไขที่ฉันคิดว่ามีโอกาสมาก (เช่น NoDataReturned, DivideByZero ฯลฯ ... ) หรือเป็นเรื่องง่ายมากที่จะตรวจสอบ (เช่นคอลเลกชันที่ว่างเปล่าหรือค่า NULL) ฉันพยายามตรวจสอบ มันล่วงหน้าและจัดการกับมันก่อนที่ฉันจะไปถึงจุดที่ฉันจะต้องได้รับการยกเว้น ฉันยอมรับว่าไม่ใช่เรื่องง่ายที่จะรู้เงื่อนไขเหล่านี้ล่วงหน้าบางครั้งพวกเขาจะปรากฏเฉพาะเมื่อรหัสอยู่ภายใต้การทดสอบอย่างเข้มงวด


0

save()วิธีต้องโยนข้อยกเว้น

เลเยอร์ที่อยู่ด้านบนสุดควรตรวจจับและแจ้งผู้ใช้โดยไม่ยกเลิกโปรแกรมเว้นแต่ว่าเป็น Unix like โปรแกรมบรรทัดคำสั่งในกรณีนี้การยกเลิก

ค่าส่งคืนไม่ใช่วิธีที่ดีในการจัดการข้อยกเว้น

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