ลองใช้ catch-blocks กันดีไหม?


15

ฉันมักจะพบว่าตัวเองต่อสู้กับเรื่องนี้ ... พยายามหาสมดุลระหว่างลอง / จับและรหัสไม่เป็นระเบียบลามกของแท็บวงเล็บและข้อยกเว้นถูกโยนกลับกองโทรเช่นมันฝรั่งร้อน ตัวอย่างเช่นฉันมีแอพที่ฉันกำลังพัฒนาอยู่ตอนนี้ซึ่งใช้ SQLite ฉันมีอินเทอร์เฟซฐานข้อมูลที่เป็นนามธรรมการเรียกใช้ SQLite และแบบจำลองที่ยอมรับสิ่งต่าง ๆ ที่จะเข้า / ออกจากฐานข้อมูล ... ดังนั้นหาก / เมื่อเกิดข้อยกเว้น SQLite ขึ้นมามันจะต้องถูกโยนขึ้นสู่รุ่น (ซึ่งเรียกว่า ) ใครจะต้องส่งมันให้ใครก็ตามที่เรียกว่า AddRecord / DeleteRecord / อะไรก็ตาม ...  

ฉันเป็นแฟนตัวยงของข้อยกเว้นซึ่งตรงกันข้ามกับการส่งคืนรหัสข้อผิดพลาดเนื่องจากรหัสข้อผิดพลาดสามารถถูกละเว้นลืม ฯลฯ ได้ในขณะที่การจัดการข้อยกเว้นเป็นหลัก (ได้รับฉันสามารถจับและย้ายได้ทันที ... ) ฉัน บางอย่างจะต้องมีวิธีที่ดีกว่าสิ่งที่ฉันเกิดขึ้นตอนนี้

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


ภาษาโปรแกรมใด
Apalala

2
ในขณะนี้ C # แต่ฉันพยายามคิดโดยทั่วไป
trycatch

C # ไม่ได้บังคับให้มีการประกาศข้อยกเว้นที่ทำให้เกิดข้อผิดพลาดขึ้นซึ่งทำให้ง่ายต่อการจัดการกับข้อยกเว้นในกรณีที่สมเหตุสมผลและหลีกเลี่ยงโปรแกรมเมอร์ที่ถูกล่อลวงให้จับมันโดยไม่ต้องจัดการ Anders Hejlsberg ผู้ออกแบบ C # ทำให้คดีต่อต้านข้อยกเว้นที่ตรวจสอบแล้วในบทความนี้artima.com/intv/handcuffs.html
Apalala

คำตอบ:


14

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

นอกจากนี้แทนที่จะโยน SQLException ไปจนถึงโมเดล (หรือแย่กว่านั้นคือ UI) แต่ละเลเยอร์ควรจับข้อยกเว้นที่รู้จักและตัด / กลายพันธุ์ / แทนที่ด้วยข้อยกเว้นที่เหมาะสมกับเลเยอร์นั้น:

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

สิ่งนี้จะช่วย จำกัด จำนวนของข้อยกเว้นที่คุณกำลังมองหาในแต่ละเลเยอร์และช่วยให้คุณรักษาระบบข้อยกเว้นที่มีการจัดระเบียบ


ฉันทำการแก้ไขสองสามคำถาม Q ต้นฉบับไม่ดี .. คำถามของฉันคือเพิ่มเติมเกี่ยวกับวิธีรักษารหัสให้สะอาดในขณะที่จัดการกับความพยายามและการจับทั้งหมดและอะไรก็ตาม มันเริ่มรู้สึกยุ่งเหยิงมากเมื่อลอง catch catch block ได้ทุกที่ ...
trycatch

1
@Ed ไม่ได้รบกวนคุณหรือว่า UI หรือรุ่นของคุณกำลังดึงดูด "SQLException" อยู่ใช่ไหม สำหรับฉันมันไม่เกี่ยวข้องกันมาก สำหรับแต่ละคนฉันคิดว่า
Nicole

1
@Ed ซึ่งอาจเรียงเป็น OK ในภาษาที่ไม่ได้ตรวจสอบข้อยกเว้น แต่ในภาษาที่มีการตรวจสอบข้อยกเว้นน่าเกลียดจริง ๆ ที่มีthrows SQLExceptionในวิธีการที่ไม่ได้หมายความว่า SQL เกี่ยวข้อง และจะเกิดอะไรขึ้นถ้าคุณตัดสินใจว่าการดำเนินการบางอย่างควรไปที่ที่เก็บไฟล์ ตอนนี้คุณต้องประกาศthrows SQLException, IOExceptionฯลฯ มันจะออกไปจากมือ
Mike Daniels

1
@Ed ต่อผู้ใช้ SQLException ไม่ได้มีความหมายมากนักโดยเฉพาะอย่างยิ่งหากระบบของคุณสามารถรองรับการคงอยู่หลายประเภทนอกเหนือจากฐานข้อมูลเชิงสัมพันธ์ ในฐานะโปรแกรมเมอร์ gui ฉันค่อนข้างจะต้องจัดการกับ DataNotFoundException มากกว่าชุดข้อยกเว้นระดับต่ำทั้งชุด บ่อยครั้งที่ข้อยกเว้นสำหรับไลบรารี่ระดับต่ำเป็นเพียงส่วนหนึ่งของชีวิตปกติที่อยู่เหนือขึ้นไปในกรณีนี้พวกเขาจะจัดการได้ง่ายกว่าเมื่อระดับของนามธรรมตรงกับแอปพลิเคชัน
Newtopian

1
@ Newtopian - ฉันไม่เคยบอกว่ามีข้อยกเว้นดิบให้กับผู้ใช้ สำหรับผู้ใช้ปลายทางข้อความ 'โปรแกรมได้หยุดทำงาน' ก็เพียงพอแล้วในขณะที่บันทึกข้อยกเว้นไว้ในที่ที่มีประโยชน์สำหรับการสนับสนุนเพื่อรับ ในโหมดดีบั๊กมีประโยชน์ในการแสดงข้อยกเว้น คุณไม่ควรใส่ใจทุกข้อยกเว้นที่เป็นไปได้ที่ API สามารถส่งได้เว้นแต่คุณจะมีตัวจัดการเฉพาะสำหรับข้อยกเว้นดังกล่าว เพียงปล่อยให้ตัวจัดการข้อยกเว้นที่ไม่จัดการของคุณจัดการกับมันทั้งหมด
Ed James

9

ข้อยกเว้นอนุญาตให้มีการเขียนโค้ดที่สะอาดกว่าเนื่องจากส่วนใหญ่จะดูแลเคสปกติและเคสพิเศษสามารถจัดการได้ในภายหลังแม้ในบริบทที่แตกต่างกัน

กฎสำหรับการจัดการ (การจับ) ข้อยกเว้นคือต้องทำตามบริบทที่สามารถทำอะไรกับมันได้ แต่นั่นมีการยกเว้น:

ข้อยกเว้นจะต้องถูกจับที่ขอบเขตของโมดูล (ขอบเขตเลเยอร์พิเศษ) และแม้ว่าจะมีเพียงการโยนระดับสูงยกเว้นที่มีความหมายกับผู้โทร แต่ละโมดูลและเลเยอร์จะต้องซ่อนรายละเอียดการนำไปใช้งานแม้จะเกี่ยวกับข้อยกเว้น (โมดูลฮีปอาจโยน HeapFull แต่ไม่เคย ArrayIndexOutOfBounds)

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


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

คุณสามารถใช้หลักการ @Ed James และฉันพูดถึงภายในเลเยอร์หรือโมดูล แทนที่จะเรียก SQLite โดยตรงจากทั่วทุกสถานมีวิธีการ / ฟังก์ชั่นสองสามอย่างที่พูดคุยกับ SQLite และกู้คืนจากข้อยกเว้นหรือแปลให้เป็นแบบทั่วไป หากธุรกรรมเกี่ยวข้องกับแบบสอบถามหลายรายการและการอัปเดตคุณไม่จำเป็นต้องจัดการกับข้อยกเว้นที่เป็นไปได้ทุกข้อในแต่ละรายการ: try-catch ภายนอกหนึ่งรายการสามารถแปลข้อยกเว้นได้ นอกจากนี้คุณยังสามารถย้ายการอัปเดตไปยังฟังก์ชั่นของตนเองเพื่อการจัดการที่ง่ายขึ้น
Apalala

1
สิ่งนี้จะเข้าใจได้ง่ายขึ้นด้วยตัวอย่างโค้ด ..
คลิกโหวต

นี่อาจเป็นคำถามที่เหมาะสมกว่าสำหรับ stackoverflow ตั้งแต่เริ่มต้น
Apalala

5

ตามกฎทั่วไปคุณควรตรวจจับข้อยกเว้นเฉพาะ (เช่น IOException) เท่านั้นและเฉพาะในกรณีที่คุณมีสิ่งเฉพาะที่ต้องทำเมื่อคุณได้รับข้อยกเว้น

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

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

การตัดข้อยกเว้นมีประโยชน์เมื่อคุณจำเป็นต้องทำการยกเว้นในระบบแบบกระจายและไคลเอนต์ไม่มีคำจำกัดความของความผิดฝั่งเซิร์ฟเวอร์


การจับและการยกเว้นความเงียบเป็นสิ่งที่น่าทำ หากโปรแกรมเสียหายมันควรผิดพลาดโดยมีข้อยกเว้นและการย้อนกลับ ควรยุ่งเหยิงพร้อมบันทึกสิ่งต่าง ๆ อย่างเงียบ ๆ แต่ทำให้ข้อมูลยุ่งเหยิง
S.Lott

1
@ S.Lott ฉันยอมรับว่าไม่ควรปิดเสียงความเงียบเพราะพวกเขาพบว่ามันน่ารำคาญ แต่การกระแทกแอปพลิเคชันค่อนข้างรุนแรง มีหลายกรณีที่เป็นไปได้ที่จะตรวจจับข้อยกเว้นและรีเซ็ตระบบในสถานะที่เป็นที่รู้จักในกรณีเช่นนี้การจัดการทั่วโลกของทุกอย่างมีประโยชน์มาก อย่างไรก็ตามหากกระบวนการรีเซ็ตล้มเหลวในทางที่ไม่สามารถจัดการได้อย่างปลอดภัยใช่แล้วปล่อยให้มันพังนั้นดีกว่าการเกาะหัวในทราย
Newtopian

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

@ Newtopian: "การห่อ" ยกเว้นและ "เขียนใหม่" ลดการรั่วไหลของ abstractions พวกเขายังคงฟองอย่างเหมาะสม "ถ้าฉันไม่รู้ว่าจะเกิดอะไรขึ้นกับฉันฉันจะตอบโต้ได้อย่างไรฉันพบว่าตัวเองมักใช้ตาข่ายดักจับที่กว้างกว่านี้มาก" นี่คือสิ่งที่เราแนะนำให้คุณหยุดทำ คุณไม่จำเป็นต้องจับทุกอย่าง 80% ของเวลาสิ่งที่ถูกต้องคือไม่จับอะไรเลย 20% ของเวลามีการตอบสนองที่มีความหมาย
S.Lott

1
@ Newtopian ฉันคิดว่าเราต้องแยกความแตกต่างระหว่างข้อยกเว้นซึ่งโดยปกติจะถูกโยนโดยวัตถุดังนั้นจึงควรห่อและข้อยกเว้นที่เกิดขึ้นเนื่องจากข้อบกพร่องในรหัสของวัตถุและไม่ควรห่อ
Winston Ewert

4

ลองนึกภาพว่าคุณกำลังเขียนคลาสสแต็ก คุณไม่ได้ใส่รหัสการจัดการข้อยกเว้นใด ๆ ในชั้นเรียนซึ่งอาจทำให้เกิดข้อยกเว้นดังต่อไปนี้

  1. ArrayIndexError - ยกระดับเมื่อผู้ใช้พยายามป๊อปอัพจากสแต็กเปล่า
  2. NullPtrException - ยกขึ้นเนื่องจากข้อผิดพลาดในการใช้งานทำให้เกิดความพยายามในการอ้างอิงการอ้างอิงเป็นโมฆะ

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

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

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

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

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

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

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