หนึ่งควรตรวจสอบโมฆะถ้าเขาไม่คาดหวังโมฆะ?


113

เมื่อสัปดาห์ที่แล้วเรามีข้อโต้แย้งที่รุนแรงเกี่ยวกับการจัดการโมฆะในชั้นบริการของแอปพลิเคชันของเรา คำถามอยู่ในบริบท. NET แต่จะเหมือนกันใน Java และเทคโนโลยีอื่น ๆ

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

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

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

คุณทำตามแบบฝึกหัดอะไรบ้างและอะไรคือข้อโต้แย้งของคุณสำหรับหรือต่อต้านแนวทางใดแนวทางหนึ่ง?

ปรับปรุง:

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

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

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


62
หลักการของ Fox Mulder: อย่าเชื่อใจใคร
yannis

14
@YannisRizos: หลักการนั้นดีมาก - ดีมากที่ผู้สร้าง Java และ C # ปล่อยให้สภาพแวดล้อมของเวลาทำงานภาษาทำได้โดยอัตโนมัติ ดังนั้นโดยปกติไม่จำเป็นต้องทำการตรวจสอบอย่างชัดเจนว่ามีค่าว่างในรหัสของคุณในภาษาเหล่านั้น
Doc Brown

4

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

2
assert(foo != null, "foo is web control within the repeater, there's no reason to expect it to be null, etc, etc...");
zzzzBov

คำตอบ:


105

คำถามนั้นไม่มากนักว่าคุณควรตรวจสอบnullหรือปล่อยให้ runtime เกิดข้อผิดพลาด มันเป็นวิธีที่คุณควรตอบสนองต่อสถานการณ์ที่ไม่คาดคิด

ตัวเลือกของคุณคือ:

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

ไม่มีกฎทั่วไปที่หนึ่งคือทางออกที่ดีที่สุด โดยส่วนตัวฉันจะพูดว่า:

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

1
@Stilgar สำหรับผู้ใช้ไม่มีความแตกต่าง สำหรับ developper ข้อยกเว้นเฉพาะเช่น "ฐานข้อมูลเซิร์ฟเวอร์ไม่สามารถเข้าถึงได้" นั้นง่ายกว่าที่ "ตัวชี้โมฆะยกเว้น"
k3b

32
การล้มเหลวอย่างหนักและเร็วหมายความว่ามีความเสี่ยงน้อยกว่าที่จะเกิดความผิดพลาดอย่างละเอียดมากยิ่งขึ้นสิ่งต่าง ๆ เช่นการพิสูจน์ตัวตนผิดพลาดข้อมูลที่เสียหายยังคงอยู่ในที่จัดเก็บข้อมูลรั่วไหลและสิ่งอื่น ๆ ที่น่าขบขัน
Lars Viklund

1
@Stilgar: มีสองข้อกังวลที่นี่: พฤติกรรมแบบอัตโนมัติและการบำรุงรักษา ในขณะที่พฤติกรรมแบบอัตโนมัตินั้นมีความแตกต่างกันเล็กน้อยระหว่างข้อยกเว้นเฉพาะและข้อทั่วไปสำหรับการบำรุงรักษามันสามารถสร้างความแตกต่างระหว่าง "โอ้ดังนั้นนั่นเป็นสาเหตุที่สิ่งต่าง ๆ ระเบิด" และ debugathlon หลายชั่วโมง
tdammers

3
tdammers นั้นถูกต้องทุกประการ "การอ้างอิงวัตถุไม่ได้ตั้งค่าเป็นอินสแตนซ์ของวัตถุ" เป็นหนึ่งในข้อยกเว้นที่น่ารำคาญที่สุดที่ฉันเห็นด้วยความถี่มากที่สุด ฉันค่อนข้างจะเห็นข้อยกเว้นเฉพาะไม่ว่าจะเป็น ArgumentNullException ที่มีชื่อพารามิเตอร์หรือข้อยกเว้นที่กำหนดเอง "ไม่มีบันทึกโพสต์อยู่สำหรับผู้ใช้ TK421"
Sean Chase

1
@ k3b สำหรับผู้ใช้เช่นกัน "ฐานข้อมูลเซิร์ฟเวอร์ไม่สามารถเข้าถึงได้" มีประโยชน์มาก อย่างน้อยสำหรับผู้ใช้ปลายทางที่มีเงื่อนงำเล็กน้อยเกี่ยวกับปัญหาที่พบบ่อย - มันอาจช่วยให้เขาพบว่าสายเคเบิลหลวมเราเตอร์ต้องเริ่มต้นใหม่ ฯลฯ แทนที่จะโกรธกับซอฟต์แวร์รถบั๊ก
Julia Hayward

58

IMHO พยายามจัดการกับค่า Null ที่คุณไม่คาดหวังว่าจะนำไปสู่โค้ดที่ซับซ้อนเกินไป ArgumentNullExceptionหากคุณไม่ได้คาดหวังโมฆะให้ชัดเจนโดยการขว้างปา ฉันรู้สึกหงุดหงิดจริง ๆ เมื่อผู้คนตรวจสอบว่าค่านั้นเป็นโมฆะและลองเขียนโค้ดที่ไม่สมเหตุสมผล เช่นเดียวกับการใช้SingleOrDefault(หรือการสะสมที่แย่ยิ่งกว่า) เมื่อใครบางคนคาดหวังSingleและอีกหลายกรณีเมื่อผู้คนกลัว (ฉันไม่รู้จริง ๆ ) เพื่อระบุตรรกะของพวกเขาอย่างชัดเจน


แทนที่จะArgumentNullExceptionเป็นหนึ่งควรใช้สัญญารหัส (เว้นแต่ถ้ามันเป็นรหัสที่มาก่อนปี 2010 ซึ่งไม่รองรับสัญญารหัส)
Arseni Mourzenko

ฉันเห็นด้วยกับคุณ. หากไม่คาดว่าจะมีค่าว่างมันไม่ควรจัดการด้วยวิธีแปลก ๆ แต่มีการรายงานเท่านั้น
Giorgio

9
หากฉันอ่านคำถามถูกต้องการArgumentNullExceptionนับเป็น "การจัดการ" ค่า Null: จะป้องกันข้อยกเว้นการอ้างอิง Null ในภายหลัง
KutuluMike

@MichaelEdenfield: ฉันไม่คิดว่านับว่าเป็นการจัดการที่แท้จริงตามคำถามที่ให้ไว้ (เป้าหมายของเราคือไม่ให้โค้ดของคุณทำงานไม่ว่าจะเกิดอะไรขึ้น ) ตามจริงแล้วฉันมักจะลืมเขียนบล็อกที่เหมาะสมซึ่งจะส่งผ่านถ้าว่าง อย่างไรก็ตามฉันคิดว่าการขว้าง NullArgumentException เป็นแนวปฏิบัติที่ดีที่สุดและแนวปฏิบัติที่เลวร้ายที่สุดในความคิดเห็นของฉันคือการเขียนโค้ดที่ไม่สามารถจัดการ null ได้ แต่แทนที่จะโยนการยกเว้น exception ส่งคืนเช่น null อื่นเป็นผลลัพธ์เมธอด (หรือสตริงว่าง หรือข้ามการเรียกใช้เมธอดหากประเภทส่งคืนเป็นโมฆะเป็นต้น)
empi

2
@Giorgio ArgumentNullExceptionทำให้มันชัดเจนมากว่าปัญหาคืออะไรและวิธีการแก้ไข C # ไม่มีแนวโน้มที่จะใช้ "undefined" หากคุณโทรหาบางอย่างในแบบที่ไม่ได้กำหนดวิธีการโทรของคุณก็ไม่สมเหตุสมผล ข้อยกเว้นเป็นวิธีที่เหมาะสมในการระบุว่า ตอนนี้บางครั้งฉันจะสร้างวิธีการวิเคราะห์คำที่ส่งคืนค่า null หากintไม่สามารถแยกวิเคราะห์ (ตัวอย่าง) แต่นั่นเป็นกรณีที่ผลลัพธ์ที่ไม่สามารถวิเคราะห์ได้คือความเป็นไปได้ทางกฎหมายมากกว่าข้อผิดพลาดในการใช้งาน ดูยังบทความนี้
Kyralessa

35

นิสัยของการตรวจสอบสำหรับnullประสบการณ์ของผมมาจากอดีต C หรือ C ++ นักพัฒนาในภาษาที่คุณมีโอกาสที่ดีของการซ่อนข้อผิดพลาดอย่างรุนแรงเมื่อไม่ได้NULLตรวจสอบ ดังที่คุณทราบใน Java หรือ C # มีความแตกต่างกันการใช้การอ้างอิงแบบ null โดยไม่ตรวจสอบnullจะทำให้เกิดข้อยกเว้นดังนั้นข้อผิดพลาดจะไม่ถูกซ่อนอย่างลับๆตราบใดที่คุณไม่เพิกเฉยต่อการโทร ดังนั้นในกรณีส่วนใหญ่การตรวจสอบอย่างชัดเจนnullไม่สมเหตุสมผลและรหัสเกินความจำเป็น นั่นเป็นเหตุผลที่ฉันไม่คิดว่าการตรวจสอบว่างเปล่าจะเป็นนิสัยที่ดีในภาษาเหล่านั้น (อย่างน้อยไม่ใช่แบบทั่วไป)

แน่นอนว่ามีข้อยกเว้นจากกฎดังกล่าวนี่คือสิ่งที่ฉันคิดได้:

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

  • คุณต้องการทำให้โปรแกรมของคุณ "ล้มเหลวเร็วขึ้น" ตัวอย่างเช่นคุณต้องการตรวจสอบค่า Null ของพารามิเตอร์ Constructor ซึ่งการอ้างอิงวัตถุจะถูกเก็บไว้ในตัวแปรสมาชิกเท่านั้นและใช้ในภายหลัง

  • คุณสามารถจัดการกับสถานการณ์ของการอ้างอิงที่เป็นโมฆะได้อย่างปลอดภัยโดยไม่มีความเสี่ยงจากความผิดพลาดที่ตามมาและไม่ปกปิดข้อผิดพลาดร้ายแรง

  • คุณต้องการการส่งสัญญาณข้อผิดพลาดที่แตกต่างอย่างสิ้นเชิง (ตัวอย่างเช่นการส่งคืนรหัสข้อผิดพลาด) ในบริบทปัจจุบันของคุณ


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

@pdr: อีกเหตุผลในการตรวจสอบคือตรวจสอบให้แน่ใจว่าตรวจพบเสมอ
Giorgio

13
"อดีตนักพัฒนา C" บางที "ผู้พัฒนา C ++ เดิม" เฉพาะในกรณีที่ผู้พัฒนา C กำลังกู้คืน ในฐานะนักพัฒนา C ++ ที่ทันสมัยฉันจะสงสัยอย่างมากเกี่ยวกับรหัสที่ใช้ตัวชี้เปล่าหรือการจัดสรรแบบไดนามิกที่ไม่ระมัดระวัง ในความเป็นจริงฉันถูกล่อลวงให้เปลี่ยนอาร์กิวเมนต์และบอกว่า C ++ ไม่ประสบปัญหาที่ OP อธิบายเนื่องจากตัวแปรไม่มีคุณสมบัติ null-ness พิเศษเพิ่มเติมที่ Java และ C # มี
Kerrek SB

7
ย่อหน้าแรกของคุณเป็นเพียงครึ่งหนึ่งของเรื่อง: ใช่ถ้าคุณใช้การอ้างอิงแบบ null ใน C # คุณจะได้รับการยกเว้นในขณะที่ใน C ++ คุณจะได้รับพฤติกรรมที่ไม่ได้กำหนด แต่ในดีเขียน C # คุณจะติดอยู่กับไกลอ้างอิง nullable มากขึ้นกว่าในดีเขียน c ++
James McNellis

การห่อหุ้มที่ดีกว่าคำตอบนี้จะดีกว่า
Radarbob

15

ใช้assertsเพื่อทดสอบและระบุ pre / postconditions และ invariants สำหรับรหัสของคุณ

มันทำให้ง่ายขึ้นมากที่จะเข้าใจสิ่งที่รหัสคาดหวังและสิ่งที่จะสามารถจัดการได้

การยืนยัน IMO เป็นการตรวจสอบที่เหมาะสมเนื่องจากเป็น:

  • มีประสิทธิภาพ (มักไม่ใช้ในรีลีส)
  • สั้น ๆ (ปกติจะเป็นบรรทัดเดียว) และ
  • ชัดเจน (ละเมิดเงื่อนไขล่วงหน้านี่เป็นข้อผิดพลาดในการเขียนโปรแกรม)

ฉันคิดว่ารหัสเอกสารด้วยตนเองเป็นสิ่งสำคัญดังนั้นการตรวจสอบจึงเป็นสิ่งที่ดี การป้องกันการโปรแกรมล้มเหลวทำให้การบำรุงรักษาง่ายขึ้นซึ่งเป็นเวลาส่วนใหญ่ที่ใช้ไป

ปรับปรุง

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

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


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

1
ดีรู้สึกฟรีใช้ assert ^ h ^ h ^ hconditional โยนฟังก์ชั่นที่ใช้งานในการเปิดตัวเช่นกันถ้าคุณต้องการที่ ฉันพลิกของตัวเองค่อนข้างบ่อย
Macke

7

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


28
ฉันหวังว่าคุณหมายถึงล้มเหลวในช่วงต้นล้มเหลวอย่างรวดเร็วไม่บ่อย ...
Empi

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

@MichaelBorgwardt ไม่ใช่ว่าการติดตามสแต็กมีไว้เพื่ออะไร
R0MANARMY

6
@ R0MANARMY: การติดตามสแต็กไม่ได้ช่วยเมื่อค่า Null ถูกบันทึกลงในบางฟิลด์และ NullPointerException ถูกส่งออกไปในภายหลัง
Michael Borgwardt

@ R0MANARMY แม้ว่าคุณจะสามารถเชื่อถือหมายเลขบรรทัดในการติดตามสแต็ก (ในหลายกรณีคุณไม่สามารถทำได้) บางบรรทัดมีตัวแปรหลายตัวที่อาจเป็นโมฆะ
Ohad Schneider

6
  • การตรวจสอบ null ควรเป็นลักษณะที่สองของคุณ

  • API ของคุณจะถูกใช้โดยบุคคลอื่นและความคาดหวังของพวกเขาจะแตกต่างจากของคุณ

  • การทดสอบหน่วยทั่ว ๆ ไปโดยปกติเน้นถึงความจำเป็นในการตรวจสอบการอ้างอิงแบบ null

  • การตรวจสอบ nulls ไม่ได้บ่งบอกถึงเงื่อนไขตามเงื่อนไข หากคุณกังวลว่าการตรวจสอบ null จะทำให้โค้ดของคุณอ่านง่ายขึ้นคุณอาจต้องพิจารณาสัญญารหัส. NET

  • พิจารณาติดตั้ง ReSharper (หรือคล้ายกัน) เพื่อค้นหารหัสของคุณสำหรับการตรวจสอบการอ้างอิงโมฆะที่ขาดหายไป


การใช้สัญญาโค้ดสำหรับเงื่อนไขเบื้องต้นนั้นเป็นเพียงแค่ข้อความรับรองที่มีเงื่อนไข
CVn

ใช่ฉันเห็นด้วย แต่ฉันก็คิดว่ารหัสนั้นดูดีกว่าเมื่อใช้สัญญารหัสนั่นคือการอ่านง่ายขึ้น
CodeART

4

ฉันจะพิจารณาคำถามจากมุมมองของความหมายเช่นถามตัวเองว่าตัวชี้ NULL หรืออาร์กิวเมนต์อ้างอิงว่าง

หากฟังก์ชันหรือเมธอด foo () มีอาร์กิวเมนต์ x เป็นประเภท T, x สามารถบังคับหรือไม่ก็ได้

ใน C ++ คุณสามารถผ่านอาร์กิวเมนต์ที่จำเป็นได้

  • ค่าเช่น void foo (T x)
  • การอ้างอิงเช่น void foo (T & x)

ในทั้งสองกรณีคุณไม่มีปัญหาในการตรวจสอบตัวชี้ NULL ดังนั้นในหลายกรณีคุณไม่จำเป็นต้องตรวจสอบตัวชี้โมฆะเลยเพื่อหาอาร์กิวเมนต์ที่จำเป็น

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

void foo(T* x)

อีกทางเลือกหนึ่งคือการใช้ตัวชี้สมาร์ทเช่น

void foo(shared_ptr<T> x)

ในกรณีนี้คุณอาจต้องการตรวจสอบตัวชี้ในรหัสเพราะมันสร้างความแตกต่างสำหรับเมธอด foo () ไม่ว่า x จะมีค่าหรือไม่มีค่าเลย

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

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

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

ใน Java คุณมีตัวเลือกน้อยลงเพราะวัตถุทั้งหมดจะถูกส่งผ่านโดยใช้การอ้างอิงซึ่งอาจเป็นโมฆะ อีกครั้งถ้าค่า Null แสดงถึงค่า NONE ของอาร์กิวเมนต์ที่เป็นทางเลือกดังนั้นการตรวจสอบค่า null จะเป็นส่วนหนึ่งของตรรกะการใช้งาน

หากค่า null เป็นอินพุตที่ไม่ถูกต้องแสดงว่าคุณมีข้อผิดพลาดและฉันจะใช้ข้อควรพิจารณาที่คล้ายกันกับกรณีของพอยน์เตอร์ C ++ เนื่องจากใน Java คุณสามารถตรวจจับข้อยกเว้นตัวชี้โมฆะได้วิธีที่ 1 ด้านบนจะเทียบเท่ากับวิธีที่ 2 หากเมธอด foo () ยกเลิกการระบุอาร์กิวเมนต์อินพุตทั้งหมดในพา ธ การเรียกใช้ทั้งหมด (ซึ่งอาจเกิดขึ้นบ่อยครั้งสำหรับเมธอดขนาดเล็ก)

ที่สรุป

  • ทั้งใน C ++ และ Java การตรวจสอบว่าอาร์กิวเมนต์ตัวเลือกเป็นโมฆะเป็นส่วนหนึ่งของตรรกะของโปรแกรมหรือไม่
  • ใน C ++ ฉันจะตรวจสอบอาร์กิวเมนต์ที่จำเป็นเสมอเพื่อให้แน่ใจว่ามีการโยนข้อยกเว้นที่เหมาะสมเพื่อให้โปรแกรมสามารถจัดการกับมันได้อย่างมีประสิทธิภาพ
  • ใน Java (หรือ C #) ฉันจะตรวจสอบข้อโต้แย้งที่บังคับถ้ามีเส้นทางการดำเนินการที่จะไม่เกิดขึ้นเพื่อให้มีรูปแบบที่ "แข็งเร็วกว่า"

แก้ไข

ขอบคุณ Stilgar สำหรับรายละเอียดเพิ่มเติม

ในกรณีของคุณดูเหมือนว่าคุณมีโมฆะเป็นผลมาจากวิธีการ อีกครั้งฉันคิดว่าคุณควรชี้แจง (และแก้ไข) ความหมายของวิธีการของคุณก่อนที่คุณจะตัดสินใจ

ดังนั้นคุณมีวิธี m1 () วิธีเรียก m2 () และ m2 () ส่งกลับการอ้างอิง (ใน Java) หรือตัวชี้ (ใน C ++) ไปยังวัตถุบางอย่าง

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

อีกทางเลือกหนึ่งคือการคืนค่า Null เป็นส่วนหนึ่งของซีแมนทิกส์ของ method m2 () นั่นคือมีผลลัพธ์ที่เป็นทางเลือกซึ่งควรมีการบันทึกไว้ในเอกสารคู่มือ Javadoc ของวิธีการ ในกรณีนี้มันก็โอเคที่จะมีค่าเป็นศูนย์ วิธี m1 () ควรตรวจสอบว่าเป็นค่าอื่น ๆ : ไม่มีข้อผิดพลาดที่นี่ แต่อาจตรวจสอบว่าผลลัพธ์เป็นโมฆะเป็นเพียงส่วนหนึ่งของตรรกะของโปรแกรม


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

ดังนั้นผลลัพธ์ที่ส่งคืนโดยเมธอดคือการอ้างอิงไปยังอ็อบเจ็กต์และ null คือค่าที่ใช้เพื่อแสดงผลลัพธ์: NOT FOUND ฉันเข้าใจถูกต้องหรือไม่
Giorgio

ฉันได้อัปเดตคำถามพร้อมรายละเอียดเพิ่มเติมแล้ว
Stilgar

3

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

หากคุณสามารถดำเนินการต่อได้อย่างปลอดภัยโดยการแทนที่ค่าอื่น (การรวบรวมที่ว่างเปล่าพูดหรือค่าเริ่มต้นหรืออินสแตนซ์) ดังนั้นโดยทั้งหมดให้ทำเช่นนั้น ตัวอย่างของ @ Shahbaz จาก World of Warcraft ซึ่งมีการแทนที่หนึ่งภาพสำหรับอีกภาพหนึ่งตกอยู่ในหมวดหมู่นั้น ภาพตัวเองน่าจะเป็นแค่การตกแต่งและไม่มีผลกระทบการทำงาน (ใน debug build ฉันจะใช้สีกรีดร้องเพื่อดึงความสนใจไปที่ความจริงที่ว่าการทดแทนเกิดขึ้น แต่ขึ้นอยู่กับลักษณะของแอปพลิเคชันเป็นอย่างมาก) บันทึกรายละเอียดเกี่ยวกับข้อผิดพลาด แต่คุณรู้ว่ามันกำลังเกิดขึ้น มิฉะนั้นคุณจะซ่อนข้อผิดพลาดที่คุณอาจต้องการทราบ การซ่อนจากผู้ใช้เป็นสิ่งหนึ่ง (สมมติว่าการประมวลผลสามารถดำเนินการต่อได้อย่างปลอดภัย) ซ่อนจากนักพัฒนาค่อนข้างอื่น

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

นอกจากนี้ยังเป็นที่น่าสังเกตว่าการตอบสนองที่เหมาะสมอาจแตกต่างกันมากระหว่างการทดสอบ / QA / การตรวจแก้จุดบกพร่องและการสร้าง / การสร้างการเผยแพร่ ในการสร้าง debug ฉันจะล้มเหลวในช่วงต้นและหนักด้วยข้อความแสดงข้อผิดพลาดอย่างละเอียดเพื่อให้ชัดเจนโดยสมบูรณ์ว่ามีปัญหาและอนุญาตให้ post-mortem เพื่อกำหนดสิ่งที่ต้องทำเพื่อแก้ไข การผลิตสร้างเมื่อต้องเผชิญกับข้อผิดพลาดที่กู้คืนได้อย่างปลอดภัยอาจจะชอบการกู้คืน


แน่นอนว่ามีข้อผิดพลาดทั่วไปจัดการขึ้นโซ่ ปัญหาเกี่ยวกับบันทึกคืออาจไม่มีใครตรวจสอบได้เป็นเวลานาน
Stilgar

@Stilgar หากไม่มีใครตรวจสอบบันทึกแสดงว่าไม่มีปัญหากับการมีอยู่หรือไม่มีเช็คว่าง
CVN

2
มีปัญหา ผู้ใช้อาจไม่สังเกตเห็นว่าระบบทำงานไม่ถูกต้องเป็นเวลานานหากมีการตรวจสอบ null ที่เพียงซ่อนข้อผิดพลาดและเขียนลงในบันทึก
Stilgar

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

1
ฉันจะไปกับความล้มเหลวอย่างรวดเร็วและเร็วแม้ในการผลิตเว้นแต่คุณจะสามารถบันทึกและรายงานข้อผิดพลาด (โดยไม่ต้องพึ่งพาผู้ใช้ที่เข้าใจมากเกินไป) การซ่อนข้อบกพร่องที่ปรับใช้ของคุณจากผู้ใช้เป็นสิ่งหนึ่งการซ่อนพวกเขาจากตัวคุณเองนั้นเป็นสิ่งที่อันตราย นอกจากนี้การอนุญาตให้ผู้ใช้อยู่กับบั๊กที่ลึกซึ้งสามารถทำลายความมั่นใจในแอปพลิเคชันของคุณ บางครั้งมันก็เป็นการดีกว่าที่จะกัดกระสุนและให้พวกเขารู้ว่ามีข้อผิดพลาดและคุณแก้ไขมันอย่างรวดเร็ว
Merlyn Morgan-Graham

2

แน่นอนว่าNULLไม่ได้คาดว่าแต่มีข้อบกพร่องเสมอ!

ฉันเชื่อว่าคุณควรตรวจสอบNULLว่าคุณไม่คาดหวังหรือไม่ ให้ฉันยกตัวอย่างให้คุณ (แม้ว่าพวกเขาจะไม่ได้อยู่ในจาวา)

  • ใน World of Warcraft เนื่องจากบั๊กภาพของหนึ่งในนักพัฒนาปรากฏบนคิวบ์สำหรับวัตถุจำนวนมาก ลองนึกภาพถ้าเอ็นจิ้นกราฟิกไม่คาดหวังพ NULLอยน์เตอร์ก็จะมีข้อผิดพลาดในขณะที่มันกลายเป็นประสบการณ์ที่ไม่เป็นอันตรายถึงตายสำหรับผู้ใช้ อย่างน้อยที่สุดคือคุณจะไม่สูญเสียการเชื่อมต่อของคุณโดยไม่คาดคิดหรือจุดบันทึกใด ๆ ของคุณ

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

  • ลองนึกภาพโปรแกรมรับจ่ายเงินของธนาคาร อินเทอร์เฟซทำการตรวจสอบและส่งข้อมูลอินพุตไปยังส่วนพื้นฐานเพื่อทำการโอนเงิน หากส่วนหลักคาดว่าจะไม่ได้รับNULLจากนั้นบั๊กในอินเทอร์เฟซอาจทำให้เกิดปัญหาในการทำธุรกรรมอาจเป็นไปได้ว่าเงินบางส่วนที่อยู่ตรงกลางหลงทาง (หรือแย่กว่านั้นคือทำซ้ำ)

    โดย "ปัญหา" ฉันหมายถึงความผิดพลาด (เช่นเดียวกับความผิดพลาดจากความผิดพลาด (พูดว่าโปรแกรมเขียนใน C ++) ไม่ใช่ข้อยกเว้นตัวชี้โมฆะ) ในกรณีนี้การทำธุรกรรมจะต้องย้อนกลับหากพบ NULL (ซึ่งแน่นอนว่าเป็นไปไม่ได้หากคุณไม่ได้ตรวจสอบตัวแปรเทียบกับ NULL!)

ฉันแน่ใจว่าคุณได้รับความคิด

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

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


ตัวอย่างที่สองของคุณหักล้างจุดของคุณ ในการทำธุรกรรมธนาคารหากมีสิ่งใดผิดพลาดการทำธุรกรรมควรยกเลิก เป็นอย่างแม่นยำเมื่อคุณครอบคลุมข้อผิดพลาดที่เงินสามารถสูญหายหรือทำซ้ำ การตรวจสอบโมฆะจะเป็นเช่น "ลูกค้าส่งเงินเป็นโมฆะ ... ดีฉันจะส่ง $ 10 (เทียบเท่ากับภาพของนักพัฒนา)"
Stilgar

@Stilgar ตรงกันข้าม! การตรวจสอบค่า NULL จะเป็นการยกเลิกพร้อมข้อความ ไม่ได้ตรวจสอบเป็นโมฆะจะเป็นความผิดพลาด ตอนนี้ความผิดพลาดในการเริ่มต้นของการทำธุรกรรมอาจไม่เลว แต่ในช่วงกลางอาจเป็นความหายนะ (ผมไม่ได้พูดโอนเงินผ่านธนาคารควรจะจัดการกับข้อผิดพลาดเหมือนเกมเพียงแค่ความจริงที่ว่ามันควรจะจัดการกับมัน)
Shahbaz

2
จุดรวมของ "การทำธุรกรรม" คือมันไม่สามารถเสร็จสมบูรณ์ครึ่งหนึ่ง :) หากมีข้อผิดพลาดก็จะถูกย้อนกลับ
Stilgar

นี่คือคำแนะนำที่น่ากลัวสำหรับทุกสิ่งที่จำเป็นต้องใช้ความหมายของธุรกรรม คุณควรทำให้ธุรกรรมทั้งหมดของคุณเป็นแบบ atomic และ idempotent ไม่เช่นนั้นข้อผิดพลาดใด ๆ ที่คุณทำจะช่วยรับประกันการสูญเสียหรือการทำซ้ำทรัพยากร (เงิน) หากคุณต้องจัดการกับการทำงานที่ไม่ใช่แบบอะตอมมิกหรือไม่ใช้ idempotent คุณควรหุ้มมันอย่างระมัดระวังในเลเยอร์ที่รับประกันพฤติกรรมแบบอะตอมมิกและ idempotent (แม้ว่าคุณจะต้องเขียนเลเยอร์เหล่านั้นด้วยตนเอง) คิดอยู่เสมอว่าจะเกิดอะไรขึ้นถ้าดาวตกชนกับเว็บเซิร์ฟเวอร์ในขณะที่การทำธุรกรรมนี้เกิดขึ้น
Merlyn Morgan-Graham

1
@ MerlynMorgan-Graham ฉันทำเช่นนั้น หวังว่าจะช่วยขจัดความสับสน
Shahbaz

2

ในฐานะที่เป็นคำตอบอื่น ๆ ชี้หากคุณไม่ได้คาดหวังให้ชัดเจนโดยการขว้างปาNULL ArgumentNullExceptionในมุมมองของฉันเมื่อคุณทำการดีบั๊กโครงการจะช่วยให้คุณค้นพบข้อบกพร่องในตรรกะของโปรแกรมของคุณได้เร็วขึ้น

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

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


1

ทุกคนnullในระบบของคุณคือระเบิดเวลาฟ้องที่รอการค้นพบ ตรวจสอบnullที่ขอบเขตที่รหัสของคุณโต้ตอบกับรหัสที่คุณไม่ได้ควบคุมและห้ามใช้nullรหัสของคุณเอง สิ่งนี้ทำให้คุณมีปัญหาโดยไม่ต้องเชื่อถือรหัสต่างประเทศอย่างไร้เดียงสา ใช้ข้อยกเว้นหรือประเภทMaybe/ บางNothingประเภทแทน


0

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

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


-1

เนื่องจากFail early, fail fastตามที่ระบุโดย @DeadMG (@empi) ไม่สามารถใช้งานได้เนื่องจากเว็บแอปพลิเคชันต้องทำงานได้ดีภายใต้ข้อบกพร่องคุณควรบังคับใช้กฎ

ไม่มีข้อยกเว้นการจับโดยไม่ต้องเข้าสู่ระบบ

เพื่อให้คุณในฐานะที่เป็นผู้พัฒนาซอฟต์แวร์ตระหนักถึงปัญหาที่อาจเกิดขึ้นโดยการตรวจสอบบันทึก

หากคุณกำลังใช้เฟรมเวิร์กการบันทึกเช่น log4net หรือ log4j คุณสามารถตั้งค่าเอาท์พุตการบันทึกพิเศษเพิ่มเติม (aka appender) ซึ่งจะส่งข้อผิดพลาดและข้อผิดพลาดที่ร้ายแรงถึงคุณ เพื่อให้คุณรับทราบข้อมูล

[อัพเดต]

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

ถ้าตกลงผู้ใช้ของหน้าเห็นข้อความผิดพลาดที่เป็นความลับคุณสามารถตั้งค่า appender ที่วาง errorlog สำหรับการประมวลผลหน้าในตอนท้ายของหน้า

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


1
คำถามคือเกิดอะไรขึ้นกับหน้า?
Stilgar

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

"ตั้งแต่ต้นล้มเหลวล้มเหลวเร็วตามที่ระบุไว้โดย @DeadMG (@empi) ไม่สามารถใช้งานได้เนื่องจากเว็บแอปพลิเคชันต้องทำงานได้ดีภายใต้ข้อบกพร่องที่คุณควรบังคับใช้กฎ": หนึ่งสามารถจับข้อยกเว้นทั้งหมด (catch (Throwable t)) และ เปลี่ยนเส้นทางไปยังหน้าข้อผิดพลาดบางอย่าง สิ่งนี้จะเป็นไปได้ไหม
จอร์โจ

-1

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

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

ยิ่งไปกว่านั้นถ้าใครบางคนต้องทำงานร่วมกับการยืนยันรหัสของคุณบอกเขาเกี่ยวกับสมมติฐานที่คุณทำในระหว่างการออกแบบ / เขียนรหัสของคุณ (ในกรณีนี้: เฮ้เพื่อนวัตถุนี้จะต้องนำเสนอ!) ซึ่งทำให้ง่ายต่อการบำรุงรักษา


คุณกรุณาช่วยเขียนเรื่องลงคะแนนได้ไหม ฉันขอขอบคุณการอภิปราย Thanx
GeT

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

-1

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

แก้ไข: หากคุณได้รับโมฆะเมื่อคุณไม่คาดหวังว่ามีบางอย่างผิดปกติ - โปรแกรมควรตาย


-1

ขึ้นอยู่กับการใช้ภาษาของข้อยกเว้น ข้อยกเว้นช่วยให้คุณสามารถดำเนินการบางอย่างเพื่อป้องกันความเสียหายของข้อมูลหรือปล่อยทรัพยากรในกรณีที่ระบบของคุณเข้าสู่สถานะหรือเงื่อนไขที่ไม่คาดคิด สิ่งนี้แตกต่างจากการจัดการข้อผิดพลาดซึ่งเป็นเงื่อนไขที่คุณสามารถคาดการณ์ได้ ปรัชญาของฉันคือคุณควรมีข้อยกเว้นน้อยที่สุดเท่าที่จะทำได้ มันเป็นเสียงรบกวน วิธีการของคุณสามารถทำสิ่งที่สมเหตุสมผลโดยการจัดการข้อยกเว้น? มันสามารถกู้คืนได้หรือไม่ สามารถซ่อมแซมหรือป้องกันความเสียหายเพิ่มเติมได้หรือไม่ หากคุณเพียงแค่จะจับข้อยกเว้นแล้วกลับจากวิธีการหรือล้มเหลวในทางที่ไม่เป็นอันตรายอื่น ๆ แล้วก็ไม่มีจุดในการจับยกเว้น คุณจะเพิ่มสัญญาณรบกวนและความซับซ้อนให้กับวิธีการของคุณเท่านั้นและคุณอาจซ่อนแหล่งที่มาของความผิดพลาดในการออกแบบของคุณ ในกรณีนี้ฉันจะปล่อยฟองความผิดพลาดขึ้นและถูกจับโดยวงด้านนอก หากคุณสามารถทำบางอย่างเช่นซ่อมแซมวัตถุหรือทำเครื่องหมายว่าเสียหายหรือปล่อยทรัพยากรที่สำคัญบางอย่างเช่นไฟล์ล็อคหรือโดยการปิดซ็อกเก็ตอย่างหมดจดนี่เป็นการใช้ข้อยกเว้นที่ดี หากคุณคาดหวังว่า NULL จะปรากฏขึ้นบ่อยครั้งเป็นกรณีขอบที่ถูกต้องคุณควรจัดการเรื่องนี้ในการไหลของตรรกะปกติด้วยคำสั่ง if-the-else หรือ switch-case หรืออะไรก็ตามไม่ใช่การจัดการข้อยกเว้น ตัวอย่างเช่นเขตข้อมูลที่ถูกปิดใช้งานในรูปแบบอาจถูกตั้งค่าเป็น NULL ซึ่งแตกต่างจากสตริงว่างแทนเขตข้อมูลที่ว่างเปล่า นี่คือพื้นที่ที่คุณต้องใช้วิจารณญาณที่ดีและสามัญสำนึก ฉันไม่เคยได้ยินกฎง่ายๆที่ดีสำหรับวิธีการจัดการกับปัญหานี้ในทุกสถานการณ์ หากคุณสามารถทำบางอย่างเช่นซ่อมแซมวัตถุหรือทำเครื่องหมายว่าเสียหายหรือปล่อยทรัพยากรที่สำคัญบางอย่างเช่นไฟล์ล็อคหรือโดยการปิดซ็อกเก็ตอย่างหมดจดนี่เป็นการใช้ข้อยกเว้นที่ดี หากคุณคาดหวังว่า NULL จะปรากฏขึ้นบ่อยครั้งเป็นกรณีขอบที่ถูกต้องคุณควรจัดการเรื่องนี้ในการไหลของตรรกะปกติด้วยคำสั่ง if-the-else หรือ switch-case หรืออะไรก็ตามไม่ใช่การจัดการข้อยกเว้น ตัวอย่างเช่นเขตข้อมูลที่ถูกปิดใช้งานในรูปแบบอาจถูกตั้งค่าเป็น NULL ซึ่งแตกต่างจากสตริงว่างแทนเขตข้อมูลที่ว่างเปล่า นี่คือพื้นที่ที่คุณต้องใช้วิจารณญาณที่ดีและสามัญสำนึก ฉันไม่เคยได้ยินกฎง่ายๆที่ดีสำหรับวิธีการจัดการกับปัญหานี้ในทุกสถานการณ์ หากคุณสามารถทำบางอย่างเช่นซ่อมแซมวัตถุหรือทำเครื่องหมายว่าเสียหายหรือปล่อยทรัพยากรที่สำคัญบางอย่างเช่นไฟล์ล็อคหรือโดยการปิดซ็อกเก็ตอย่างหมดจดนี่เป็นการใช้ข้อยกเว้นที่ดี หากคุณคาดหวังว่า NULL จะปรากฏขึ้นบ่อยครั้งเป็นกรณีขอบที่ถูกต้องคุณควรจัดการเรื่องนี้ในการไหลของตรรกะปกติด้วยคำสั่ง if-the-else หรือ switch-case หรืออะไรก็ตามไม่ใช่การจัดการข้อยกเว้น ตัวอย่างเช่นเขตข้อมูลที่ถูกปิดใช้งานในรูปแบบอาจถูกตั้งค่าเป็น NULL ซึ่งแตกต่างจากสตริงว่างแทนเขตข้อมูลที่ว่างเปล่า นี่คือพื้นที่ที่คุณต้องใช้วิจารณญาณที่ดีและสามัญสำนึก ฉันไม่เคยได้ยินกฎง่ายๆที่ดีสำหรับวิธีการจัดการกับปัญหานี้ในทุกสถานการณ์

Java ล้มเหลวในการดำเนินการจัดการข้อยกเว้นกับ "การตรวจสอบข้อยกเว้น" ที่ทุกข้อยกเว้นที่เป็นไปได้ที่อาจถูกยกขึ้นโดยวัตถุที่ใช้ในวิธีการจะต้องถูกจับหรือประกาศในส่วน "พ่น" ของวิธีการ ตรงกันข้ามกับ C ++ และ Python ที่คุณสามารถเลือกจัดการข้อยกเว้นได้ตามที่เห็นสมควร ใน Java หากคุณปรับเปลี่ยนเนื้อความของวิธีการรวมการเรียกที่อาจเพิ่มข้อยกเว้นคุณต้องเผชิญกับทางเลือกในการเพิ่มการจัดการที่ชัดเจนสำหรับข้อยกเว้นที่คุณไม่สนใจดังนั้นการเพิ่มเสียงรบกวนและความซับซ้อนให้กับรหัสของคุณหรือคุณต้อง เพิ่มข้อยกเว้นนั้นในประโยค "throws" ของวิธีการที่คุณแก้ไขซึ่งไม่เพียง แต่เพิ่มสัญญาณรบกวนและความยุ่งเหยิง แต่เปลี่ยนลายเซ็นของวิธีการของคุณ จากนั้นคุณต้องไปแก้ไขรหัสที่ใดก็ตามที่วิธีการของคุณใช้เพื่อจัดการกับข้อยกเว้นใหม่วิธีการของคุณตอนนี้อาจเพิ่มหรือเพิ่มข้อยกเว้นในส่วน "พ่น" ของวิธีการเหล่านั้นจึงเรียกผลกระทบโดมิโนของการเปลี่ยนแปลงรหัส "Catch or Specify Requirement" จะต้องเป็นหนึ่งในแง่มุมที่น่ารำคาญที่สุดของ Java ข้อยกเว้นสำหรับ RuntimeExceptions และการหาเหตุผลเข้าข้างตนเองของ Java อย่างเป็นทางการสำหรับความผิดพลาดในการออกแบบนี้คือ lame

ย่อหน้าสุดท้ายนั้นเล็กน้อยเกี่ยวกับการตอบคำถามของคุณ ฉันเกลียดชวา Java

- โนอาห์


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