ทำไมวิธีการไม่ควรโยนข้อยกเว้นที่ตรวจสอบหลายประเภท


47

เราใช้ SonarQube เพื่อวิเคราะห์โค้ด Java ของเราและมีกฎนี้ (ตั้งค่าเป็นวิกฤติ):

วิธีการสาธารณะควรมีข้อยกเว้นที่ตรวจสอบได้มากที่สุดหนึ่งรายการ

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

เพื่อรักษาความซับซ้อนสำหรับผู้โทรที่เหมาะสมวิธีการไม่ควรมีข้อยกเว้นที่ตรวจสอบมากกว่าหนึ่งประเภท "

อีกเล็กน้อยใน Sonar มีสิ่งนี้ :

วิธีการสาธารณะควรมีข้อยกเว้นที่ตรวจสอบได้มากที่สุดหนึ่งรายการ

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

เพื่อรักษาความซับซ้อนสำหรับผู้โทรที่เหมาะสมวิธีการไม่ควรมีข้อยกเว้นที่ตรวจสอบมากกว่าหนึ่งประเภท

รหัสต่อไปนี้:

public void delete() throws IOException, SQLException {      // Non-Compliant
  /* ... */
}

ควรได้รับการปรับสภาพใหม่เป็น:

public void delete() throws SomeApplicationLevelException {  // Compliant
    /* ... */
}

วิธีการแทนที่จะไม่ตรวจสอบโดยกฎนี้และได้รับอนุญาตให้โยนข้อยกเว้นที่ตรวจสอบหลายรายการ

ฉันไม่เคยเจอกฎนี้ / คำแนะนำในการอ่านของฉันเกี่ยวกับการจัดการข้อยกเว้นและได้พยายามหามาตรฐานการสนทนา ฯลฯ ในหัวข้อ สิ่งเดียวที่ฉันได้พบคือจาก CodeRach: วิธีการที่ควรยกเว้นมากที่สุด

นี่เป็นมาตรฐานที่ได้รับการยอมรับหรือไม่?


7
อะไรที่คุณคิดว่า? คำอธิบายที่คุณยกมาจาก SonarQube ดูสมเหตุสมผล คุณมีเหตุผลที่จะสงสัยหรือไม่?
Robert Harvey

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

คำตอบ:


32

ให้พิจารณาสถานการณ์ที่คุณมีรหัสที่ให้:

public void delete() throws IOException, SQLException {      // Non-Compliant
  /* ... */
}

อันตรายที่นี่คือรหัสที่คุณเขียนเพื่อโทรdelete()จะมีลักษณะดังนี้:

try {
  foo.delete()
} catch (Exception e) {
  /* ... */
}

มันก็ไม่ดีเหมือนกัน และจะถูกจับด้วยกฎอื่นที่จับค่าสถานะการยกเว้นคลาสพื้นฐาน

กุญแจสำคัญคือการไม่เขียนโค้ดที่ทำให้คุณต้องการเขียนโค้ดไม่ดีที่อื่น

กฎที่คุณพบเป็นกฎที่ค่อนข้างใช้กันทั่วไป Checkstyleมีกฎการออกแบบ:

ThrowsCount

จำกัด การโยนข้อความสั่งให้นับที่ระบุ (1 โดยค่าเริ่มต้น)

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

สิ่งนี้อธิบายปัญหาได้อย่างแม่นยำและปัญหาคืออะไรและทำไมคุณไม่ควรทำ เป็นมาตรฐานที่ได้รับการยอมรับเป็นอย่างดีว่าเครื่องมือวิเคราะห์แบบคงที่จำนวนมากจะระบุและตั้งค่าสถานะ

และในขณะที่คุณอาจทำตามการออกแบบภาษาและอาจมีบางครั้งที่มันเป็นสิ่งที่ถูกต้องที่จะทำมันเป็นสิ่งที่คุณควรเห็นและไปทันที "อืมทำไมฉันถึงทำอย่างนี้?" อาจเป็นที่ยอมรับได้สำหรับรหัสภายในที่ทุกคนได้รับการลงโทษทางวินัยมากพอที่จะไม่ทำcatch (Exception e) {}แต่บ่อยครั้งที่ฉันไม่เห็นคนถูกตัดมุมโดยเฉพาะในสถานการณ์ภายใน

อย่าทำให้คนที่ใช้คลาสของคุณต้องการเขียนโค้ดที่ไม่ดี


ฉันควรชี้ให้เห็นว่าความสำคัญของสิ่งนี้ลดลงด้วย Java SE 7 และหลังจากนั้นเนื่องจากคำสั่ง catch เดี่ยวสามารถตรวจจับข้อยกเว้นหลาย ๆ ข้อได้ (การตรวจจับชนิดข้อยกเว้นหลายรายการและข้อยกเว้น Rethrowing ด้วยการปรับปรุงการตรวจสอบประเภทจาก Oracle)

ด้วย Java 6 และก่อนหน้านี้คุณจะมีโค้ดที่ดูเหมือน:

public void delete() throws IOException, SQLException {
  /* ... */
}

และ

try {
  foo.delete()
} catch (IOException ex) {
     logger.log(ex);
     throw ex;
} catch (SQLException ex) {
     logger.log(ex);
     throw ex;
}

หรือ

try {
    foo.delete()
} catch (Exception ex) {
    logger.log(ex);
    throw ex;
}

ตัวเลือกเหล่านี้กับ Java 6 ไม่เหมาะอย่างยิ่ง วิธีแรกละเมิดแห้ง บล็อกจำนวนมากทำสิ่งเดียวกันซ้ำแล้วซ้ำอีก - ครั้งเดียวสำหรับแต่ละข้อยกเว้น คุณต้องการที่จะเข้าสู่ระบบข้อยกเว้นและ rethrow มันได้หรือไม่ ตกลง. บรรทัดของรหัสเดียวกันสำหรับแต่ละข้อยกเว้น

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

ด้วย Java 7 สิ่งนี้ไม่สำคัญเท่ากับคุณทำได้:

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

นอกจากนี้ประเภทการตรวจสอบถ้าไม่จับประเภทของข้อยกเว้นที่ถูกโยน:

public void rethrowException(String exceptionName)
throws IOException, SQLException {
    try {
        foo.delete();
    } catch (Exception e) {
        throw e;
    }
}

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

แม้จะมีการเปลี่ยนแปลงเหล่านี้ แต่เครื่องมือการวิเคราะห์แบบสแตติกจำนวนมาก (Sonar, PMD, Checkstyle) ยังคงบังคับใช้คำแนะนำสไตล์ Java 6 มันไม่ใช่เรื่องเลวร้าย ฉันมักจะเห็นด้วยกับคำเตือนเหล่านี้ว่าจะยังคงมีการบังคับใช้ แต่คุณอาจเปลี่ยนลำดับความสำคัญเป็นหลักหรือรองตามวิธีที่ทีมของคุณจัดลำดับความสำคัญ

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


การยอมรับสิ่งนี้เป็นคำตอบที่ตอบคำถามของฉันเกี่ยวกับการเป็นมาตรฐานที่ยอมรับกันดี อย่างไรก็ตามฉันยังคงอ่านการสนทนาแบบ pro / con ต่าง ๆ ที่เริ่มต้นด้วยลิงก์ Panzercrisis ที่ให้ไว้ในคำตอบของเขาเพื่อกำหนดมาตรฐานของฉัน
sdoca

"ตัวตรวจสอบชนิดจะรับรู้ว่า e อาจเป็นประเภท IOException หรือ SQLException เท่านั้น": นี่หมายความว่าอย่างไร จะเกิดอะไรขึ้นเมื่อมีการยกเว้นประเภทอื่นถูกโยนทิ้งfoo.delete()? มันยังคงถูกจับและโยนใหม่หรือไม่?
Giorgio

@Giorgio มันจะเป็นข้อผิดพลาดเวลารวบรวมถ้าdeleteโยนข้อยกเว้นตรวจสอบนอกเหนือ IOException หรือ SQLException ในตัวอย่างนั้น จุดสำคัญที่ฉันพยายามทำคือวิธีการที่เรียก rethrowException จะยังคงเป็นประเภทของข้อยกเว้นใน Java 7 ใน Java 6 ทั้งหมดที่ได้รับการรวมกันเป็นExceptionประเภททั่วไปซึ่งทำให้การวิเคราะห์แบบคงที่และตัวเข้ารหัสอื่น ๆ เศร้า

ฉันเห็น. ดูเหมือนจะซับซ้อนสำหรับฉัน ฉันจะพบว่าใช้งานง่ายกว่าในการห้ามcatch (Exception e)และบังคับให้เป็นแบบใดแบบหนึ่งcatch (IOException e)หรือcatch (SQLException e)แทน
Giorgio

@Giorgio มันเป็นขั้นตอนที่เพิ่มขึ้นจาก Java 6 เพื่อพยายามเขียนโค้ดให้ง่ายขึ้น น่าเสียดายที่ตัวเลือกในการเขียนรหัสไม่ดีจะอยู่กับเราเป็นเวลานาน จำไว้ว่า Java 7 สามารถทำได้catch(IOException|SQLException ex)แทน แต่ถ้าคุณเพิ่งจะสร้างข้อยกเว้นขึ้นอีกครั้งการอนุญาตให้ตัวตรวจสอบชนิดเพื่อเผยแพร่ชนิดที่แท้จริงของข้อยกเว้นในการลดความซับซ้อนของการทำให้โค้ดง่ายขึ้นไม่ได้เป็นเรื่องเลวร้าย

22

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

สมมติว่าเรามีวิธีที่ดึงข้อมูลจากการคงอยู่และการมีอยู่นั้นเป็นชุดของไฟล์ เนื่องจากเรากำลังติดต่อกับไฟล์เราจึงมีFileNotFoundException:

public String getData(int id) throws FileNotFoundException

ตอนนี้เรามีการเปลี่ยนแปลงข้อกำหนดและข้อมูลของเรามาจากฐานข้อมูล แทนที่จะเป็นFileNotFoundException(เนื่องจากเราไม่ได้เกี่ยวข้องกับไฟล์) ตอนนี้เราโยนSQLException:

public String getData(int id) throws SQLException

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

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

public String getData(int id) throws InvalidRecordException

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


สำหรับความรับผิดชอบเดี่ยวเราต้องคิดถึงรหัสที่ส่งข้อยกเว้นหลายอย่างที่ไม่เกี่ยวข้อง สมมติว่าเรามีวิธีการดังต่อไปนี้:

public Record parseFile(String filename) throws IOException, ParseException

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

public String readFile(String filename) throws IOException
public Record parse(String data) throws ParseException

เราแยกความรับผิดชอบในการอ่านไฟล์จากความรับผิดชอบในการแยกวิเคราะห์ข้อมูล ผลข้างเคียงของสิ่งนี้คือตอนนี้เราสามารถส่งผ่านข้อมูล String ใด ๆ ไปยังข้อมูลแยกวิเคราะห์จากแหล่งใด ๆ : ในหน่วยความจำ, ไฟล์, เครือข่าย, ฯลฯ เราสามารถทดสอบparseได้ง่ายขึ้นในตอนนี้เพราะเราไม่ต้องการไฟล์บนดิสก์ เพื่อทำการทดสอบ


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


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

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

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

+1: ฉันชอบคำตอบนี้มากโดยเฉพาะอย่างยิ่งเพราะมันแก้ปัญหาจากมุมมองการสร้างแบบจำลอง บ่อยครั้งที่มันไม่จำเป็นต้องใช้สำนวนอื่น (เช่นcatch (IOException | SQLException ex)) เพราะปัญหาที่แท้จริงอยู่ในรูปแบบของโปรแกรม / การออกแบบ
Giorgio

3

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

http://tutorials.jenkov.com/java-exception-handling/checked-or-unchecked-exceptions.html

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

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

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

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


2
ทำไมไม่เพียงแค่ประกาศการขว้างปาThrowableและทำมันให้สำเร็จแทนที่จะประดิษฐ์ลำดับชั้นทั้งหมดของคุณเอง?
Deduplicator

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

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

1
@Dupuplicator ฉันไม่ได้สนับสนุนความคิดและฉันก็ไม่จำเป็นต้องสนับสนุนมันเช่นกัน ฉันแค่พูดถึงทิศทางที่คำแนะนำของ OP ได้รับอาจมาจาก
Panzercrisis

1
ขอบคุณสำหรับลิงค์ มันเป็นจุดเริ่มต้นที่ดีในการอ่านหัวข้อนี้
sdoca

-1

การขว้างข้อยกเว้นที่ตรวจสอบหลายข้อนั้นสมเหตุสมผลเมื่อมีหลายสิ่งที่ควรทำ

ตัวอย่างเช่นสมมติว่าคุณมีวิธีการ

public void doSomething(Credentials cred, Work work) 
    throws CredentialsRequiredException, TryAgainLaterException{...}

สิ่งนี้ละเมิดกฎข้อยกเว้น pne แต่สมเหตุสมผล

น่าเสียดายที่สิ่งที่เรามักจะเกิดขึ้นคือวิธีการต่างๆ

void doSomething() 
    throws IOException, JAXBException,SQLException,MyException {...}

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

ดังนั้นจึงต้องมีกฎที่ตรวจสอบได้มากที่สุดหนึ่งข้อยกเว้น

แต่ถ้าโครงการของคุณใช้การออกแบบที่มีหลายข้อยกเว้นการตรวจสอบความหมายเต็มกฎนี้ไม่ควรใช้

Sidenote: มีบางอย่างผิดพลาดจริง ๆ เกือบทุกที่ดังนั้นใครจะคิดถึงการใช้งานบ้าง ขยาย RuntimeException แต่มีความแตกต่างระหว่าง "เราทุกคนทำผิดพลาด" และ "นี่พูดถึงระบบภายนอกและบางครั้งมันจะลงจัดการกับมัน"


1
"สิ่งที่สมเหตุสมผลหลายอย่างที่ต้องทำ" แทบจะไม่ปฏิบัติตามsrp - จุดนี้ถูกจัดวางไว้ในคำตอบก่อนหน้า
gnat

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