ยืนยันความชั่วร้ายหรือไม่ [ปิด]


199

Goผู้สร้างภาษาเขียน :

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

ความคิดเห็นของคุณเกี่ยวกับเรื่องนี้คืออะไร?


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

@allyourcode หากคุณกำลังพูดถึงreflect.DeepEqualคุณไม่จำเป็นต้องใช้มันแน่นอน สะดวก แต่เสียค่าใช้จ่ายต่อประสิทธิภาพ (การทดสอบหน่วยเป็นกรณีการใช้งานที่ดี) มิฉะนั้นคุณสามารถใช้การตรวจสอบความเท่าเทียมกันที่เหมาะสมสำหรับ "คอลเลกชัน" ของคุณโดยไม่มีปัญหามากเกินไป
Igor Dubinskiy

1
ไม่นั่นไม่ใช่สิ่งที่ฉันกำลังพูดถึง ไม่มีสิ่งเช่น slice1 == slice2 โดยไม่มีการสะท้อน ภาษาอื่น ๆ ทั้งหมดนั้นเทียบเท่ากับการทำงานขั้นพื้นฐานขั้นสูงนี้ เหตุผลเดียวที่ Go ไม่ได้มีอคติ
allyourcode

คุณสามารถเปรียบเทียบสองชิ้นโดยไม่มีการสะท้อนโดยใช้การforวนซ้ำใน Go (เช่นเดียวกับ C) มันจะดีจริง ๆ ที่มีการดำเนินการชิ้นทั่วไปแม้ว่าการเปรียบเทียบจะซับซ้อนเมื่อตัวชี้และ structs เกี่ยวข้อง
kbolino

คำตอบ:


321

ไม่ไม่มีอะไรผิดปกติassertตราบใดที่คุณใช้มันตามที่ตั้งใจไว้

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

  • ยืนยัน: ความล้มเหลวในตรรกะของโปรแกรมเอง
  • การจัดการข้อผิดพลาด: อินพุตผิดพลาดหรือสถานะระบบไม่ได้เกิดจากข้อผิดพลาดในโปรแกรม

109

ไม่มีค่าgotoมิได้assertเป็นความชั่วร้าย แต่ทั้งคู่สามารถนำไปใช้ในทางที่ผิด

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


วิธีใช้gotoอย่างชาญฉลาด
ar2015

1
@ ar2015 ค้นหาหนึ่งในรูปแบบที่ประดิษฐ์ขึ้นอย่างไร้เหตุผลที่บางคนแนะนำให้หลีกเลี่ยงgotoด้วยเหตุผลทางศาสนาล้วนๆจากนั้นเพียงใช้gotoแทนที่จะทำให้งงงวยในสิ่งที่คุณกำลังทำอยู่ ในคำอื่น ๆ : ถ้าคุณสามารถพิสูจน์ได้ว่าคุณต้องการจริงๆgotoและทางเลือกเดียวคือการออกพระราชกำหนดการโหลดของนั่งร้านไม่มีจุดหมายว่าในท้ายที่สุดจะเป็นสิ่งเดียวกัน แต่ไม่มี alterting ตำรวจไป ... gotoแล้วใช้เพียง แน่นอนว่าเงื่อนไขนี้เป็นบิต "ถ้าคุณสามารถพิสูจน์ได้ว่าคุณต้องการจริงๆgoto" บ่อยครั้งที่คนทำไม่ได้ นั่นก็ไม่ได้หมายความว่ามันเป็นสิ่งเลวร้าย
underscore_d

2
gotoใช้ในเคอร์เนล linux สำหรับการล้างรหัส
malat

61

ด้วยเหตุผลนั้นจุดพักก็ชั่วเช่นกัน

Asserts ควรใช้เป็นตัวช่วยในการดีบั๊กและไม่มีอะไรอื่นอีก "Evil" คือเมื่อคุณลองใช้พวกเขาแทนที่จะจัดการกับข้อผิดพลาด

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

พวกเขาไม่มีอะไรเกี่ยวข้องกับการจัดการข้อผิดพลาด แต่น่าเสียดายที่โปรแกรมเมอร์บางคนใช้พวกเขาในทางที่ผิดเช่นนั้นและประกาศว่าพวกเขา "ชั่วร้าย"


40

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

เนื่องจากการยืนยันฉันใช้เวลาในการเขียนโปรแกรม / การดีบักน้อยลง

ฉันยังสังเกตเห็นว่าการยืนยันช่วยฉันคิดหลายสิ่งหลายอย่างที่อาจทำลายโปรแกรมของฉัน


31

ในฐานะที่เป็นข้อมูลเพิ่มเติมไปให้ฟังก์ชั่นในpanicตัว assertนี้สามารถนำมาใช้ในสถานที่ของ เช่น

if x < 0 {
    panic("x is less than 0");
}

panicassertจะพิมพ์กองติดตามดังนั้นในบางวิธีก็มีวัตถุประสงค์ของการ


30

ควรใช้เพื่อตรวจจับข้อบกพร่องในโปรแกรม การป้อนข้อมูลของผู้ใช้ไม่เลว

หากใช้อย่างถูกต้องพวกเขาจะไม่ชั่วร้าย


13

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

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

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

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


8

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

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

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

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


5

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

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


5

ฉันชอบหลีกเลี่ยงรหัสที่ทำสิ่งต่าง ๆ ในการดีบักและปล่อย

การแบ่งดีบักเกอร์ตามเงื่อนไขและการมีข้อมูลไฟล์ / บรรทัดทั้งหมดนั้นมีประโยชน์แม้ว่านิพจน์ที่แน่นอนและค่าที่แน่นอน

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

assert(2 == ShroedingersCat.GetNumEars()); จะทำให้โปรแกรมทำสิ่งต่าง ๆ ในการดีบักและปล่อย

เราได้พัฒนาชุดของแมโครยืนยันซึ่งจะทำให้เกิดข้อยกเว้นและทำทั้งในเวอร์ชันการดีบักและรีลีส ตัวอย่างเช่นTHROW_UNLESS_EQ(a, 20);จะส่งข้อยกเว้นพร้อมข้อความ what () ที่มีทั้งไฟล์บรรทัดและค่าจริงของ a และอื่น ๆ มีเพียงแมโครเท่านั้นที่มีพลังสำหรับสิ่งนี้ ตัวดีบักอาจถูกกำหนดค่าให้หยุดที่ 'การโยน' ของประเภทข้อยกเว้นเฉพาะ


4
ร้อยละ 90 ของสถิติที่ใช้ในการโต้แย้งเป็นเท็จ
João Portela

5

ฉันไม่ชอบการยืนยันอย่างเข้มข้น ฉันจะไม่ไปไกลเท่าที่บอกว่าพวกเขาชั่วร้าย

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

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

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


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


3

ฉันเพิ่งเริ่มเพิ่มการยืนยันลงในโค้ดของฉันและนี่คือวิธีที่ฉันทำ:

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

รหัสภายในเป็นทุกอย่างอื่น ตัวอย่างเช่นฟังก์ชันที่ตั้งค่าตัวแปรในคลาสของฉันอาจถูกกำหนดเป็น

void Class::f (int value) {
    assert (value < end);
    member = value;
}

และฟังก์ชั่นที่รับอินพุตจากเครือข่ายอาจอ่านดังนี้

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

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

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


2

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

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

ทางเลือกบางอย่างเพื่อยืนยัน:

  • ใช้ดีบักเกอร์
  • คอนโซล / ฐานข้อมูล / การบันทึกอื่น ๆ
  • ข้อยกเว้น
  • การจัดการข้อผิดพลาดประเภทอื่น

อ้างอิงบางส่วน:

แม้แต่คนที่สนับสนุนการยืนยันก็คิดว่าพวกเขาควรจะใช้ในการพัฒนาไม่ใช่ในการผลิต:

คนนี้บอกว่าอ้างควรจะใช้เมื่อโมดูลได้ที่อาจเกิดความเสียหายข้อมูลที่ยังคงอยู่หลังจากที่ยกเว้นจะโยน: http://www.advogato.org/article/949.html นี่เป็นจุดที่สมเหตุสมผลอย่างไรก็ตามโมดูลภายนอกไม่ควรกำหนดความสำคัญของข้อมูลที่เสียหายต่อโปรแกรมเรียก (โดยการออก "สำหรับ" พวกเขา) วิธีที่เหมาะสมในการจัดการสิ่งนี้คือการส่งข้อยกเว้นที่ทำให้ชัดเจนว่าขณะนี้โปรแกรมอาจอยู่ในสถานะไม่สอดคล้องกัน และเนื่องจากโปรแกรมที่ดีส่วนใหญ่ประกอบด้วยโมดูล (ด้วยรหัสกาวเล็กน้อยในการปฏิบัติการหลัก) การยืนยันจึงเป็นสิ่งที่ผิดที่ต้องทำ


1

assert มีประโยชน์มากและสามารถช่วยคุณประหยัดเวลาในการย้อนรอยเมื่อเกิดข้อผิดพลาดที่ไม่คาดคิดโดยหยุดโปรแกรมที่สัญญาณแรกของปัญหา

assertในทางกลับกันมันเป็นเรื่องง่ายมากที่จะทำผิดกฎ

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

รุ่นที่ถูกต้องและเหมาะสมจะเป็นดังนี้:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

ดังนั้น ... ในระยะยาว ... ในภาพรวม ... ฉันต้องยอมรับว่าassertสามารถถูกทำร้ายได้ ฉันทำมันตลอดเวลา


1

assert กำลังถูกละเมิดเนื่องจากการจัดการข้อผิดพลาดเนื่องจากพิมพ์น้อย

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


1
ไม่เลวเกินไป :-) ข้อยกเว้นหรือไม่การยืนยันหรือไม่แฟนโกยังคงพูดถึงว่ารหัสสั้นแค่ไหน
Moshe Revah

1

ฉันรู้สึกเหมือนเตะนักเขียนที่หัวเมื่อฉันเห็นสิ่งนั้น

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

ข้อยกเว้นยังผสมผสานกับรหัสการผลิตได้ง่ายขึ้นซึ่งฉันไม่ชอบ การยืนยันง่ายกว่าที่จะสังเกตเห็นthrow new Exception("Some generic msg or 'pretend i am an assert'");


1

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


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

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

ฉันตัดสินใจตัวอย่างสถานการณ์ที่ดีที่สุด นี่เป็นเรื่องง่าย int func (int i) {ถ้า (i> = 0) {console.write ("จำนวนเป็นบวก {0}", i); } else {assert (false); // เพื่อขี้เกียจทำเชิงลบ ATM} คืนค่า i * 2; <- ฉันจะทำสิ่งนี้โดยไม่ยืนยันและเป็นข้อยกเว้นที่ดีกว่าจริง ๆ ? และโปรดจำไว้ว่ารหัสนี้จะถูกนำไปใช้ก่อนที่จะเผยแพร่

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

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

1

ใช่การอ้างสิทธิ์นั้นชั่วร้าย

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

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

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

บางครั้งมันเป็นไม้ค้ำสำหรับการออกแบบที่หัก เช่นการออกแบบของรหัสช่วยให้ผู้ใช้เรียกมันในแบบที่มันไม่ควรถูกเรียกและ assert "ป้องกัน" สิ่งนี้ แก้ไขการออกแบบ!

ฉันเขียนเกี่ยวกับเรื่องนี้เพิ่มเติมในบล็อกของฉันในปี 2005 ที่นี่: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html


0

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


5
การยืนยันเป็นสิ่งที่ดีสำหรับการประกาศเงื่อนไขล่วงหน้าที่ด้านบนของฟังก์ชั่นและหากเขียนอย่างชัดเจนให้ทำหน้าที่เป็นส่วนหนึ่งของเอกสารของฟังก์ชั่น
Peter Cordes

0

ฉันไม่เคยใช้ assert () ตัวอย่างมักแสดงสิ่งนี้:

int* ptr = new int[10];
assert(ptr);

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

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

14
newไม่เคยส่งคืนnullptrมันจะพ่น
David Stone

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