มีการตั้งค่าสถานะเพื่อระบุว่าเราควรผิดพลาดหรือไม่


64

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

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

ตัวอย่าง

public class AreaCalculator
{
    AreaCalculator(bool shouldThrowExceptions) { ... }
    CalculateArea(int x, int y)
    {
        if(x < 0 || y < 0)
        {
            if(shouldThrowExceptions) 
                throwException;
            else
                return 0;
        }
    }
}

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

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

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

นี่เป็นวิธีที่ดีในการจัดการข้อยกเว้นหรือไม่

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

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


22
c # มีข้อตกลงสำหรับการพูดคุยลองใช้การแยกวิเคราะห์นี้ ข้อมูลเพิ่มเติม: docs.microsoft.com/en-us/dotnet/standard/design-guidelines/… การตั้งค่าสถานะไม่ตรงกับรูปแบบนี้
ปีเตอร์

18
นี่คือพารามิเตอร์การควบคุมโดยทั่วไปและจะเปลี่ยนวิธีการทำงานของ internals สิ่งนี้ไม่ดีโดยไม่คำนึงถึงสถานการณ์ martinfowler.com/bliki/FlagArgument.html , softwareengineering.stackexchange.com/questions/147977/... , medium.com/@amlcurran/...
BIC

1
นอกเหนือจากความคิดเห็นลองแยกวิเคราะห์จากปีเตอร์นี่เป็นบทความที่ดีเกี่ยวกับข้อยกเว้นVexing
Linaith

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

1
ฉันค่อนข้างแน่ใจว่าสิ่งนี้ได้ถูกกล่าวถึงก่อนหน้านี้ แต่ได้รับตัวอย่างง่ายๆของพื้นที่ฉันอยากจะสงสัยว่าตัวเลขติดลบเหล่านี้มาจากไหนและคุณสามารถจัดการกับข้อผิดพลาดนั้นได้จากที่อื่น (เช่น อะไรก็ตามที่กำลังอ่านไฟล์ที่มีความยาวและความกว้างเช่น); อย่างไรก็ตาม "เรากำลังพยายามใช้อุปกรณ์เครือข่ายที่ไม่สามารถแสดงได้ในขณะนั้น" ประเด็นนี้อาจเป็นคำตอบที่แตกต่างกันโดยสิ้นเชิงนี่เป็น API ของบุคคลที่สามหรือมาตรฐานอุตสาหกรรมเช่น TCP / UDP หรือไม่
jrh

คำตอบ:


74

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

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

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


1
@Quirk มันน่าประทับใจที่เฉินจัดการฝ่าฝืนหลักการความรับผิดชอบเดี่ยวใน 3 หรือ 4 บรรทัดเท่านั้น นั่นคือปัญหาที่แท้จริง นอกจากนี้ปัญหาที่เขาพูดถึง (โปรแกรมเมอร์ไม่สามารถคิดเกี่ยวกับผลที่ตามมาของข้อผิดพลาดในแต่ละบรรทัด) มักเป็นไปได้ด้วยข้อยกเว้นที่ไม่ได้ตรวจสอบและบางครั้งก็เป็นไปได้ด้วยข้อยกเว้นที่ตรวจสอบ ฉันคิดว่าฉันเคยเห็นข้อโต้แย้งทั้งหมดที่เคยทำกับข้อยกเว้นที่ตรวจสอบแล้วและไม่ใช่ข้อใดข้อหนึ่งที่ถูกต้อง
TKK

@TKK ส่วนตัวมีบางกรณีที่ฉันพบว่าฉันชอบการตรวจสอบข้อยกเว้นใน. NET มันจะดีถ้ามีเครื่องมือวิเคราะห์แบบสแตติกระดับไฮเอนด์ที่สามารถทำให้แน่ใจว่าเอกสาร API ใดที่เป็นข้อยกเว้นที่ส่งออกมานั้นมีความถูกต้องแม้ว่าจะเป็นไปไม่ได้เลยทีเดียวโดยเฉพาะอย่างยิ่งเมื่อเข้าถึงทรัพยากรดั้งเดิม
jrh

1
@jrh ใช่มันคงจะดีถ้ามีบางสิ่งบางอย่างที่ทำให้ความปลอดภัยยกเว้นใน kludged. NET คล้ายกับความปลอดภัยของ TypeScript kludges ใน JS
TKK

47

นี่เป็นวิธีที่ดีในการจัดการข้อยกเว้นหรือไม่

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

โดยทั่วไปเมื่อเราออกแบบคลาสและ API ของพวกเขาเราควรพิจารณาสิ่งนั้น

  1. อาจมีหลายอินสแตนซ์ของคลาสที่มีการกำหนดค่าต่าง ๆ ที่ลอยอยู่ในโปรแกรมเดียวกันในเวลาเดียวกันและ,

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

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

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

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

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


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

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


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

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


ไม่ควรเป็นความรับผิดชอบของผู้โทรที่จะกำหนดวิธีดำเนินการต่อ

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


คุณไม่จำเป็นต้องตรวจสอบข้อยกเว้นจริงๆเพราะคุณต้องตรวจสอบข้อขัดแย้งก่อนที่จะเรียกวิธีการ การตรวจสอบผลลัพธ์เทียบกับศูนย์ไม่ได้แยกความแตกต่างระหว่างข้อโต้แย้งทางกฎหมาย 0, 0 และรายการเชิงลบที่ผิดกฎหมาย API นั้นน่ากลัวมากสำหรับ IMHO
BlackJack

ภาคผนวก K MS ผลักดันให้ C99 และ C ++ iostreams เป็นตัวอย่างของ API ที่ตะขอหรือธงเปลี่ยนแปลงการตอบสนองต่อความล้มเหลวอย่างรุนแรง
Deduplicator

37

พวกเขาทำงานเกี่ยวกับแอพพลิเคชั่นที่สำคัญเกี่ยวกับการบินที่ระบบไม่สามารถลงได้ ผลที่ตามมา ...

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

  • ข้อยกเว้นจะไม่ได้รับการจัดการอย่างเหมาะสมอย่างน้อยที่สุดหรืออย่างเข้มงวด

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

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

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

  • ฝึกทีมให้คาดหวังและจัดการกับข้อยกเว้นเมื่อใช้ส่วนประกอบ "กล่องดำ" และมีพฤติกรรมทั่วโลกของโปรแกรมในใจ

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

    CalculateArea(int x, int y, out ErrorCode err)

    ดังนั้นมันยากสำหรับผู้ที่มองข้ามฟังก์ชันอาจล้มเหลว แต่นี่เป็น IMHO ที่น่าเกลียดที่สุดใน C #; มันเป็นเทคนิคการเขียนโปรแกรมการป้องกันแบบเก่าจาก C ที่ไม่มีข้อยกเว้นและปกติแล้วไม่ควรจำเป็นต้องทำงานในปัจจุบัน


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

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

7
@Trilarion: หากคอมพิวเตอร์ของเที่ยวบินแสดงความสูงผิดและเครื่องบินจะพังเพราะเหตุนี้มันจะไม่ช่วยคุณได้ (โดยเฉพาะเมื่อมีระบบสำรองอยู่ที่นั่นและไม่ได้รับการแจ้งว่าจำเป็นต้องใช้เวลา) ระบบสำรองข้อมูลสำหรับคอมพิวเตอร์บนเครื่องบินไม่ใช่แนวคิดใหม่ Google สำหรับ "ระบบสำรองข้อมูลของคอมพิวเตอร์บนเครื่องบิน" ผมค่อนข้างมั่นใจว่าวิศวกรทั่วโลกมักสร้างระบบสำรองในระบบที่สำคัญยิ่งสำหรับชีวิตจริง (และถ้ามันเป็นเพราะไม่สูญเสีย ประกันภัย).
Doc Brown

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

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

13

การเขียนการทดสอบหน่วยจะซับซ้อนกว่าเล็กน้อยเนื่องจากคุณต้องทดสอบการตั้งค่าสถานะข้อยกเว้นในแต่ละครั้ง

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

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

นอกจากนี้หากมีสิ่งผิดปกติคุณไม่ต้องการรู้ทันทีหรือไม่?

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

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

ไม่ควรเป็นความรับผิดชอบของผู้โทรที่จะกำหนดวิธีดำเนินการต่อ

โทรไม่กำหนดวิธีการที่จะดำเนินการต่อ ในกรณีนี้มันจะทำก่อนเวลาโดยการตั้งค่าหรือล้างshouldThrowExceptionsและฟังก์ชั่นการทำงานตามสถานะของมัน

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

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

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

และในฐานะหนึ่งในโปรแกรมเมอร์ที่อายุมากขึ้นฉันจะขอให้คุณออกจากสนามหญ้าของฉัน ;-)


ฉันยอมรับว่าการตั้งชื่อและเจตนามีความสำคัญอย่างยิ่งและในกรณีนี้ชื่อพารามิเตอร์ที่เหมาะสมสามารถเปลี่ยนตารางได้จริงดังนั้น +1 แต่ 1. our program needs to do 1 thing, show data to user. Any other exception that doesn't stop us from doing so should be ignoredความคิดอาจนำไปสู่การตัดสินใจของผู้ใช้ตามข้อมูลที่ไม่ถูกต้อง 1 สิ่ง - เพื่อช่วยผู้ใช้ในการตัดสินใจอย่างชาญฉลาด) 2. กรณีที่คล้ายกันbool ExecuteJob(bool throwOnError = false)มักเป็นข้อผิดพลาดอย่างมากและนำไปสู่โค้ดที่ยากที่จะให้เหตุผลเพียงแค่อ่าน
Eugene Podskal

@EugenePodskal ฉันคิดว่าสมมติฐานคือ "แสดงข้อมูล" หมายถึง "แสดงข้อมูลที่ถูกต้อง" ผู้ถามไม่ได้บอกว่าผลิตภัณฑ์ที่เสร็จแล้วใช้งานไม่ได้ แต่อาจเขียนว่า "ผิด" ฉันต้องดูข้อมูลที่ยากในจุดที่สอง ฉันมีฟังก์ชั่นที่ใช้งานหนักจำนวนมากในโปรเจ็กต์ปัจจุบันของฉันที่มีสวิตช์แบบ throw / no-throw และไม่ยากที่จะให้เหตุผลมากกว่าฟังก์ชั่นอื่น ๆ แต่นั่นคือจุดข้อมูลหนึ่งจุด
Blrfl

คำตอบที่ดีฉันคิดว่าตรรกะนี้ใช้กับสถานการณ์ในวงกว้างมากกว่าเพียงแค่ OP BTW, cpp ใหม่มีรุ่นการโยน / ไม่โยนด้วยเหตุผลเหล่านี้ ฉันรู้ว่ามีความแตกต่างเล็ก ๆ น้อย ๆ แต่ ...
drjpizzle

8

ความปลอดภัยที่สำคัญและรหัส 'ปกติ' สามารถนำไปสู่แนวคิดที่แตกต่างกันอย่างมากเกี่ยวกับลักษณะที่ 'การปฏิบัติที่ดี' มีหลายอย่างทับซ้อนกัน - บางสิ่งมีความเสี่ยงและควรหลีกเลี่ยงทั้งสองอย่าง - แต่ก็ยังมีความแตกต่างที่สำคัญ หากคุณเพิ่มข้อกำหนดที่จะรับประกันว่าจะตอบสนองส่วนเบี่ยงเบนเหล่านี้จะค่อนข้างมาก

สิ่งเหล่านี้มักเกี่ยวข้องกับสิ่งที่คุณคาดหวัง:

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

    อย่างไรก็ตาม: สำหรับแผงหน้าปัดที่มีการคำนวณ g-force จนตรอกและป้องกันการคำนวณความเร็วอากาศอาจไม่สามารถยอมรับได้

บางคนมีความชัดเจนน้อยกว่า:

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

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

สิ่งนี้มีผลกับตัวอย่างของคุณอย่างไร:

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

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

จากทั้งหมดที่กล่าวมามันดูน่าสงสัยเล็กน้อยสำหรับฉันแต่ :

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


7

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

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

ที่กล่าวว่าประเภทข้อมูลที่ได้ทำให้รำ่รวยนี้ไม่ควรบังคับให้กระทำ var area_calc = new AreaCalculator(); var volume = area_calc.CalculateArea(x, y) * z;ลองนึกภาพในบางสิ่งบางอย่างเช่นพื้นที่ของคุณเช่น ดูเหมือนว่ามีประโยชน์volumeควรมีพื้นที่คูณด้วยความลึกซึ่งอาจเป็นลูกบาศก์ทรงกระบอก ฯลฯ ...

แต่จะเกิดอะไรขึ้นถ้าบริการ area_calc หยุดทำงาน จากนั้นarea_calc .CalculateArea(x, y)ส่งคืนประเภทข้อมูลที่มีข้อผิดพลาด การคูณด้วยนั้นถูกกฎหมายzหรือไม่ มันเป็นคำถามที่ดี คุณสามารถบังคับให้ผู้ใช้จัดการการตรวจสอบได้ทันที อย่างไรก็ตามนี่จะเป็นการแบ่งตรรกะด้วยการจัดการข้อผิดพลาด

var area_calc = new AreaCalculator();
var area_result = area_calc.CalculateArea(x, y);
if (area_result.bad())
{
    //handle unhappy path
}
var volume = area_result.value() * z;

VS

var area_calc = new AreaCalculator();
var volume = area_calc.CalculateArea(x, y) * z;
if (volume.bad())
{
    //handle unhappy path
}

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

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

อีกวิธีหนึ่งvolumeอาจเป็นเพียงชนิดข้อมูลธรรมดา - เป็นตัวเลข แต่จะเกิดอะไรขึ้นกับเงื่อนไขข้อผิดพลาด อาจเป็นไปได้ว่ามูลค่าโดยปริยายแปลงหากอยู่ในสภาพที่มีความสุข หากอยู่ในสภาพที่ไม่มีความสุขอาจส่งคืนค่าเริ่มต้น / ข้อผิดพลาด (สำหรับพื้นที่ 0 หรือ -1 อาจดูเหมือนสมเหตุสมผล) อีกทางหนึ่งมันอาจส่งข้อยกเว้นที่ด้านข้างของขอบเขตส่วนต่อประสาน / ภาษานี้

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}
var volume = foo();
if (volume <= 0)
{
    //handle error
}

เมื่อเทียบกับ

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}

try { var volume = foo(); }
catch(...)
{
    //handle error
}

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

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

ทางออกที่ดีที่สุดจะเป็นวิธีหนึ่งที่ช่วยให้สถานการณ์ทั้งหมด (ภายในเหตุผล)

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

สิ่งที่ต้องการ:

Rich::value_type value_or_default(Rich&, Rich::value_type default_value = ...);
bool bad(Rich&);
...unhappy path report... bad_state(Rich&);
Rich& assert_not_bad(Rich&);
class Rich
{
public:
   typedef ... value_type;

   operator value_type() { assert_not_bad(*this); return ...value...; }
   operator X(...) { if (bad(*this)) return ...propagate badness to new value...; /*operate and generate new value*/; }
}

//check
if (bad(x))
{
    var report = bad_state(x);
    //handle error
}

//rethrow
assert_not_bad(x);
var result = (assert_not_bad(x) + 23) / 45;

//propogate
var y = x * 23;

//implicit throw
Rich::value_type val = x;
var val = ((Rich::value_type)x) + 34;
var val2 = static_cast<Rich::value_type>(x) % 3;

//default value
var defaulted = value_or_default(x);
var defaulted_to = value_or_default(x, 55);

@TobySpeight ยุติธรรมเพียงพอสิ่งเหล่านี้มีบริบทที่อ่อนไหวและมีขอบเขต
Kain0_0

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

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

3

ฉันจะตอบจากมุมมองของ C ++ ฉันค่อนข้างมั่นใจว่าแนวคิดหลักทั้งหมดสามารถถ่ายโอนไปยัง C # ได้

ดูเหมือนว่าสไตล์ที่คุณชื่นชอบคือ "มักจะโยนข้อยกเว้น":

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

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

ดังนั้นบางไลบรารี (เช่น<filesystem>) ใช้สิ่งที่ C ++ เรียกว่า "dual API" หรือสิ่งที่ C # เรียกTry-Parseรูปแบบ (ขอบคุณPeterสำหรับคำแนะนำ!)

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

bool TryCalculateArea(int x, int y, int& result) {
    if (x < 0 || y < 0) {
        return false;
    }
    result = x * y;
    return true;
}

int a1 = CalculateArea(x, y);
int a2;
if (TryCalculateArea(x, y, a2)) {
    // use a2
}

คุณสามารถดูปัญหาเกี่ยวกับ "dual APIs" ได้ทันที: การทำสำเนารหัสจำนวนมากไม่มีคำแนะนำสำหรับผู้ใช้ว่า API คือ "สิทธิ์" ที่จะใช้และผู้ใช้จะต้องเลือกอย่างหนักระหว่างข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์ ( CalculateArea) และความเร็ว ( TryCalculateArea) เนื่องจากรุ่นที่เร็วกว่านั้นใช้"negative side lengths"ข้อยกเว้นที่เป็นประโยชน์ของเราและทำให้มันแบนลงในไร้ประโยชน์false- "มีบางอย่างผิดปกติอย่าถามฉันว่าอะไรหรือที่ไหน" (บาง APIs คู่ใช้ชนิดข้อผิดพลาดที่แสดงออกมากขึ้นเช่นint errnoหรือ C ++ 's std::error_codeแต่ที่ยังคงไม่ได้บอกคุณที่ข้อผิดพลาดที่เกิดขึ้น - เพียงแค่ว่ามันไม่ได้เกิดขึ้นที่ใดที่หนึ่ง.)

หากคุณไม่สามารถตัดสินใจได้ว่าโค้ดของคุณควรทำงานอย่างไรคุณสามารถตัดสินใจเลือกผู้โทรได้!

template<class F>
int CalculateArea(int x, int y, F errorCallback) {
    if (x < 0 || y < 0) {
        return errorCallback(x, y, "negative side lengths");
    }
    return x * y;
}

int a1 = CalculateArea(x, y, [](auto...) { return 0; });
int a2 = CalculateArea(x, y, [](int, int, auto msg) { throw Exception(msg); });
int a3 = CalculateArea(x, y, [](int, int, auto) { return x * y; });

นี่คือสิ่งที่ผู้ร่วมงานของคุณกำลังทำอยู่ ยกเว้นว่าเขาแยกตัว "ข้อผิดพลาดจัดการ" ออกเป็นตัวแปรทั่วโลก:

std::function<int(const char *)> g_errorCallback;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorCallback("negative side lengths");
    }
    return x * y;
}

g_errorCallback = [](auto) { return 0; };
int a1 = CalculateArea(x, y);
g_errorCallback = [](const char *msg) { throw Exception(msg); };
int a2 = CalculateArea(x, y);

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

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

bool g_errorViaException;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorViaException ? throw Exception("negative side lengths") : 0;
    }
    return x * y;
}

g_errorViaException = false;
int a1 = CalculateArea(x, y);
g_errorViaException = true;
int a2 = CalculateArea(x, y);

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

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

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
#ifdef NEXCEPTIONS
        return 0;
#else
        throw Exception("negative side lengths");
#endif
    }
    return x * y;
}

// Now these two function calls *must* have the same behavior,
// which is a nice property for a program to have.
// Improves understandability.
//
int a1 = CalculateArea(x, y);
int a2 = CalculateArea(x, y);

ตัวอย่างของสิ่งที่ทำงานด้วยวิธีนี้เป็นassertแมโครใน C และ C ++ NDEBUGซึ่งเงื่อนไขในพฤติกรรมของแมโครพรีโพรเซสเซอร์


หากหนึ่งในผลตอบแทนstd::optionalจากTryCalculateArea()แทนมันเป็นเรื่องง่ายที่จะรวมการดำเนินงานของทั้งสองส่วนของอินเตอร์เฟซสองในหนึ่งเดียวฟังก์ชั่นแม่แบบที่มีเวลารวบรวมธง
Deduplicator

@Deduplicator: std::expectedบางทีด้วย มีเพียงstd::optionalเว้นแต่ฉันเข้าใจผิดโซลูชั่นที่นำเสนอของคุณก็จะยังคงต้องทนทุกข์ทรมานจากสิ่งที่ฉันกล่าวว่าผู้ใช้จะต้องให้เป็นทางเลือกยากระหว่างข้อความผิดพลาดที่มีประโยชน์และความเร็วเพราะรุ่นได้เร็วขึ้นใช้เวลาที่เป็นประโยชน์ของเรา"negative side lengths"ข้อยกเว้นและ flattens มันลงมาเป็นไร้ประโยชน์false- " มีบางอย่างผิดปกติอย่าถามฉันว่าอะไรหรือที่ไหน "
Quuxplusone

นั่นเป็นเหตุผลที่libc ++ <ระบบแฟ้ม>ไม่จริงบางสิ่งบางอย่างที่ใกล้เคียงกับรูปแบบของเพื่อนร่วมงานของ OP: มันท่อstd::error_code *ecตลอดทางลงผ่านทุกระดับของ API if (ec == nullptr) throw something; else *ec = some error codeและจากนั้นที่ด้านล่างไม่เทียบเท่าคุณธรรมของ (มันสรุปสิ่งที่เกิดขึ้นจริงifเป็นสิ่งที่เรียกว่าErrorHandlerแต่มันเป็นแนวคิดพื้นฐานเดียวกัน)
Quuxplusone

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

1
ความคิดที่ดีมากมายมีอยู่ในคำตอบนี้ ... ต้องการ upvotes มากขึ้น :-)
cmaster

1

ฉันรู้สึกว่าควรพูดถึงว่าเพื่อนร่วมงานของคุณมีรูปแบบมาจากไหน

ปัจจุบัน, C # public bool TryThing(out result)มีรูปแบบ วิธีนี้ช่วยให้คุณได้รับผลลัพธ์ในขณะที่ยังแจ้งให้คุณทราบว่าผลลัพธ์นั้นเป็นค่าที่ถูกต้องหรือไม่ (ตัวอย่างเช่นintค่าทั้งหมดเป็นผลลัพธ์ที่ถูกต้องสำหรับMath.sum(int, int)แต่ถ้าค่านั้นมีมากเกินไปผลลัพธ์ที่เฉพาะเจาะจงนี้อาจเป็นขยะ) นี่เป็นรูปแบบที่ค่อนข้างใหม่

ก่อนที่outคำสำคัญคุณจะต้องโยนข้อยกเว้น (แพงและผู้โทรต้องจับมันหรือฆ่าโปรแกรมทั้งหมด) สร้าง struct พิเศษ (คลาสก่อนคลาสหรือ generics เป็นเรื่องจริง) สำหรับผลลัพธ์แต่ละรายการเพื่อแสดงค่า และข้อผิดพลาดที่เป็นไปได้ (ใช้เวลานานในการสร้างและซอฟต์แวร์ bloats) หรือส่งคืนค่า "ข้อผิดพลาด" เริ่มต้น (ซึ่งอาจไม่ใช่ข้อผิดพลาด)

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

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


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

@MathieuGuindon Yeay แต่ GetTry ยังไม่เป็นที่รู้จักกันดี / เป็นที่ยอมรับและแม้ว่ามันจะเป็นฉันไม่แน่ใจว่ามันจะถูกนำมาใช้ ท้ายที่สุดส่วนหนึ่งของสารตะกั่วจนถึง Y2K ก็คือการจัดเก็บสิ่งที่มีขนาดใหญ่กว่า 0-99 นั้นไม่สามารถยอมรับได้
Tezra

0

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

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

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

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

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

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


0

ตัวอย่างเฉพาะนี้มีคุณสมบัติที่น่าสนใจที่อาจส่งผลต่อกฎ ...

CalculateArea(int x, int y)
{
    if(x < 0 || y < 0)
    {
        if(shouldThrowExceptions) 
            throwException;
        else
            return 0;
    }
}

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

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

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

// Configurable dependencies
AreaCalculator(PreconditionFailureStrategy strategy)

CalculateArea(int x, int y)
{
    if (x < 0 || y < 0) {
        return this.strategy.fail(0);
    }
    // ...
}

พวกเขาทำงานเกี่ยวกับแอพพลิเคชั่นที่สำคัญเกี่ยวกับการบินที่ระบบไม่สามารถลงได้

คณะกรรมการการจราจรและความปลอดภัยแห่งชาตินั้นดีจริงๆ ฉันอาจแนะนำเทคนิคการใช้ทางเลือกให้กับ graybeards แต่ฉันไม่อยากเถียงกับพวกเขาเกี่ยวกับการออกแบบ bulkheads ในระบบย่อยการรายงานข้อผิดพลาด

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


ฉันชอบข้อเสนอแนะของวิธีอื่นในการรักษาความยืดหยุ่นที่ต้องการในขณะที่ยังคงความทันสมัย
drjpizzle

-1

วิธีการอย่างใดอย่างหนึ่งจัดการกับข้อยกเว้นหรือพวกเขาไม่จำเป็นต้องมีธงในภาษาเช่น C #

public int Method1()
{
  ...code

 return 0;
}

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

public int Method1()
{
try {  
...code
}
catch {}
 ...Handle error 
}
return 0;
}

ในกรณีนี้หากสิ่งที่ไม่ดีเกิดขึ้นใน ... รหัสวิธีที่ 1 คือการจัดการปัญหาและโปรแกรมควรดำเนินการต่อไป

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


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

-1

วิธีการนี้แบ่งปรัชญา "ล้มเหลวเร็วล้มเหลวยาก"

ทำไมคุณต้องการล้มเหลวอย่างรวดเร็ว:

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

ข้อเสียของการไม่ล้มเหลวอย่างรวดเร็วและยาก:

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

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

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

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

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

จุดหนึ่งที่คอมมิชชันในความคิดเห็น:

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

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

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


3
มันได้รับการชี้ให้เห็นในคำตอบอื่น ๆ ที่ล้มเหลวอย่างรวดเร็วและยากที่ไม่พึงประสงค์ในแอปพลิเคชันที่สำคัญเมื่อฮาร์ดแวร์ที่คุณใช้เป็นเครื่องบิน
HAEM

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

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

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