IllegalStateException ที่ดีกว่าหรือการดำเนินการเมธอดแบบเงียบคืออะไร [ปิด]


16

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

สิ่งที่ควรเป็นกฎทั่วไปเมื่อเมธอดไม่ควรถูกเรียกในบางสถานการณ์ แต่การดำเนินการมันไม่เป็นอันตรายต่อโปรแกรมโดยทั่วไป?


21
อย่าใช้ข้อยกเว้นในการทำงานของโมเดลที่ไม่ได้รับการยกเว้น
Alexander - Reinstate Monica

1
เฮ้! ฉันไม่คิดว่ามันจะซ้ำกัน คำถามของฉันเกี่ยวกับ IllegalStateException เพิ่มเติมและคำถามข้างต้นเกี่ยวกับการใช้ข้อยกเว้นโดยทั่วไป
x2bool

1
แทนที่จะเป็นเพียงแค่ความล้มเหลวอย่างเงียบ ๆ ทำไมไม่บันทึกความผิดปกติไว้เป็นคำเตือน? แน่นอนว่า Java ที่คุณใช้รองรับระดับการบันทึกที่แตกต่างกัน (ข้อผิดพลาด, เตือน, INFO, DEBUG)
Eric Seastrand

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

2
คำพูดของวัน: idempotence
Jules

คำตอบ:


37

ไม่มีกฎ ขึ้นอยู่กับว่าคุณต้องการทำให้ API "รู้สึก" ทั้งหมดเป็นอย่างไร

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

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

ดังนั้นถ้ามันขึ้นอยู่กับฉันฉันจะข้ามข้อยกเว้น


15
คำตอบนี้เตือนเราว่าบางครั้งเป็นความคิดที่ดีที่จะร่างการเปลี่ยนสถานะสำหรับการโต้ตอบที่เรียบง่าย

6
เมื่อคำนึงถึงเรื่องนี้การโยนข้อยกเว้นจะดูอวดรู้และไม่ยุติธรรม มันจะทำให้ API รู้สึกเหมือนกับสังคมในการพูดคุยกับเด็กที่น่ารำคาญบนรถโรงเรียน > ข้อยกเว้นไม่ได้ออกแบบมาเพื่อลงโทษคุณ: พวกเขาได้รับการออกแบบมาเพื่อบอกคุณบางสิ่งบางอย่างที่ไม่คาดคิดเกิดขึ้น ฉันเองจะขอบคุณมากสำหรับMediaPlayer.stop()วิธีการที่โยนIllegalStateExceptionแทนที่จะใช้เวลาหลายชั่วโมงในการแก้จุดบกพร่องรหัสและสงสัยว่าทำไมนรก "ไม่มีอะไรเกิดขึ้น" (เช่น "มันแค่ใช้งานไม่ได้")
เดินทางที่ผิดพลาด

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

1
StopOrThrow()ฟังดูน่ากลัว หากคุณกำลังจะลงที่ซอยทำไมไม่ใช้รูปแบบมาตรฐานของTryStop()? นอกจากนี้โดยทั่วไปเมื่อพฤติกรรมของ API ไม่ชัดเจน (โดยส่วนใหญ่เป็น) นักพัฒนาไม่คาดว่าจะคาดเดาหรือทดลองเพียงแค่พวกเขาคาดว่าจะดูเอกสาร นั่นเป็นเหตุผลที่ฉันชอบ MSDN มาก
MetaFight

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

17

มีการกระทำที่แตกต่างกันสองประเภทที่เราอาจต้องการดำเนินการ:

  1. ทดสอบพร้อมกันว่ามีบางสิ่งอยู่ในสถานะหนึ่งและเปลี่ยนเป็นสถานะอื่น

  2. ตั้งค่าบางสิ่งเป็นสถานะเฉพาะโดยไม่คำนึงถึงสถานะก่อนหน้า

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

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


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

bool TryStop()และvoid Stop()ตัวอย่างโค้ดจะทำให้เรื่องนี้เป็นคำตอบที่ดีอย่างแท้จริง
RubberDuck

2
@RubberDuck: นั่นคงไม่ใช่รูปแบบที่ดี ชื่อTryStopจะบอกเป็นนัยว่าไม่ควรโยนข้อยกเว้นถ้าผู้เล่นไม่สามารถถูกบังคับให้อยู่ในสถานะหยุด พยายามที่จะหยุดผู้เล่นเมื่อมันหยุดแล้วไม่ได้เป็นเงื่อนไขที่พิเศษ แต่เป็นเงื่อนไขที่ผู้โทรอาจมีความสนใจมา
supercat

1
ถ้าอย่างนั้นฉันก็เข้าใจผิดคำตอบของคุณ @supercat
RubberDuck

2
ฉันต้องการชี้ให้เห็นว่าการทดสอบและการตั้งค่าด้วยวิธีนี้ไม่ใช่การละเมิดกฎหมาย demeter หาก API ยังมีวิธีการทดสอบสถานะการเล่นโดยไม่ต้องเปลี่ยน isPlaying()นี้อาจจะเป็นวิธีการที่แตกต่างกันอย่างสิ้นเชิงกล่าว
candied_orange

11

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

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


11

จุดประสงค์ของการยกเว้นไม่ใช่เพื่อส่งสัญญาณว่ามีบางสิ่งไม่ดีเกิดขึ้น มันเป็นสัญญาณว่า

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

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

ดังนั้นคุณไม่ควรทิ้งข้อยกเว้นในสถานการณ์นี้


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

5

ดูด้วยวิธีนี้:

หากไคลเอนต์โทร Stop () เมื่อผู้เล่นไม่ได้เล่น Stop () จะสำเร็จโดยอัตโนมัติเนื่องจากโปรแกรมเล่นอยู่ในสถานะหยุดทำงาน


3

กฎคือคุณทำสิ่งที่สัญญาของวิธีการของคุณต้องการ

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

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

try {
    player.stop();
} catch(IllegalStateException e) {
    // do nothing, player was already stopped
}

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

ผู้เล่นอาจจะสังเกตได้และแล้วอาจแจ้งให้ผู้สังเกตการณ์เกี่ยวกับบางสิ่งบางอย่าง - เช่นแจ้งสังเกตการณ์เมื่อมัน transitons จากplayingที่จะไปstopping stoppedในกรณีนี้การมีวิธีการโยนข้อยกเว้นไม่จำเป็น การโทรstopจะไม่ทำอะไรเลยถ้าผู้เล่นหยุดแล้วและการโทรเมื่อมันไม่หยุดจะแจ้งให้ผู้สังเกตการณ์ทราบเกี่ยวกับการเปลี่ยน

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

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


3

มีกลยุทธ์ทั่วไปง่าย ๆ ที่จะช่วยคุณตัดสินใจ

พิจารณาว่าจะจัดการข้อยกเว้นอย่างไรถ้าถูกส่งออกไป

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

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

สิ่งนี้ไม่เพียงนำไปใช้กับข้อยกเว้น แต่การแยกประเภทใด ๆ : โดยพื้นฐานแล้วหากจำเป็นต้องไม่มีความแตกต่างที่สังเกตได้ในพฤติกรรมก็ไม่จำเป็นต้องแยกแขนงออก

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


2

นี่คือวิธีที่ android ใช้งาน: MediaPlayer

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


1

สิ่งที่ควรเป็นกฎทั่วไปเมื่อเมธอดไม่ควรถูกเรียกในบางสถานการณ์ แต่การดำเนินการมันไม่เป็นอันตรายต่อโปรแกรมโดยทั่วไป?

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

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

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

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

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

แก้ไข:

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


ไม่ควรถูกเรียกเพราะการโทรเป็นการระบุข้อบกพร่องในตัวเรียก
user253751

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

@meriton หากเป็นผู้ฟังที่คลิกปุ่ม UI คำตอบจะชัดเจน - "ทำทุกสิ่งที่คุณต้องการให้เกิดขึ้นหากกดปุ่ม" เห็นได้ชัดว่าไม่ใช่มันเป็นสิ่งที่อยู่ภายในแอปพลิเคชัน
user253751

@immibis - ฉันแก้ไขคำตอบของฉัน ฉันหวังว่ามันจะช่วย
Jim

1

สิ่งที่ทำMediaPlayer.play()และMediaPlayer.stop()ทำ - พวกเขาฟังเหตุการณ์สำหรับการป้อนข้อมูลของผู้ใช้หรือพวกเขาเป็นจริงวิธีการที่เริ่มประเภทสตรีมสื่อในระบบ? หากพวกเขาทั้งหมดเป็นผู้ฟังสำหรับการป้อนข้อมูลของผู้ใช้ก็เป็นเหตุผลที่เหมาะสมสำหรับพวกเขาที่จะไม่ทำอะไรเลย (แม้ว่ามันจะเป็นความคิดที่ดี แต่ถ้าพวกเขาส่งผลกระทบต่อรูปแบบ UI ของคุณคือการควบคุมแล้วพวกเขาก็อาจจะโยนIllegalStateExceptionเพราะผู้เล่นจะต้องมีการจัดเรียงของบางส่วนisPlaying=trueหรือisPlaying=falseรัฐ (มันไม่จำเป็นต้องเป็นธงบูลีนที่เกิดขึ้นจริงเช่นการเขียนที่นี่) และอื่น ๆ เมื่อคุณเรียกMediaPlayer.stop()เมื่อisPlaying=falseที่ วิธีการจริงไม่สามารถ "หยุด" MediaPlayerเพราะวัตถุไม่ได้อยู่ในสถานะที่เหมาะสมที่จะหยุด - ดูคำอธิบายของjava.lang.IllegalStateException ระดับ:

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

ทำไมการโยนข้อยกเว้นถึงทำได้ดี

ผู้คนจำนวนมากดูเหมือนจะคิดว่าข้อยกเว้นนั้นไม่ดีโดยทั่วไปเนื่องจากไม่ควรถูกโยนทิ้ง (เช่นJoel on Software ) อย่างไรก็ตามสมมติว่าคุณมีMediaPlayerGUI.notifyPlayButton()สายที่เรียกใช้MediaPlayer.play()และMediaPlayer.play()เรียกใช้รหัสอื่น ๆ และมีบางส่วนที่มันเชื่อมต่อกับเช่นPulseAudioแต่ฉัน (ผู้พัฒนา) ไม่รู้ว่าอยู่ที่ไหนเพราะฉันไม่ได้เขียนรหัสนั้นทั้งหมด

จากนั้นวันหนึ่งขณะทำงานกับรหัสฉันคลิก "เล่น" และไม่มีอะไรเกิดขึ้น หากMediaPlayerGUIController.notifyPlayButton()บันทึกบางอย่างอย่างน้อยฉันสามารถดูในบันทึกและตรวจสอบว่าการคลิกที่ปุ่มได้รับการลงทะเบียนจริง ... แต่ทำไมมันไม่เล่น? ในความเป็นจริงมีบางอย่างผิดปกติกับเครื่องห่อหุ้ม PulseAudio ที่MediaPlayer.play()เรียกใช้ ฉันคลิกปุ่ม "เล่น" อีกครั้งและครั้งนี้ฉันได้รับIllegalStateException:

 Exception in thread "main" java.lang.IllegalStateException: MediaPlayer already in play state.

     at org.superdupermediaplayer.MediaPlayer.play(MediaPlayer.java:16)
     at org.superdupermediaplayer.gui.MediaPlayerGUIController.notifyPlayButton(MediaPlayerGUIController.java:25)
     at org.superdupermediaplayer.gui.MediaPlayerGUI.main(MediaPlayerGUI.java:14)

โดยการดูที่การติดตามสแต็กนั้นฉันสามารถละเว้นทุกอย่างก่อนMediaPlayer.play()เมื่อทำการดีบั๊กและใช้เวลาในการหาสาเหตุเช่นไม่มีข้อความใดถูกส่งไปยัง PulseAudio เพื่อเริ่มสตรีมเสียง

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


0

ฉันชอบการออกแบบ API ในแบบที่ทำให้ผู้บริโภคเกิดข้อผิดพลาดได้ยากขึ้นหรือเป็นไปไม่ได้ ตัวอย่างเช่นแทนที่จะมีMediaPlayer.play()และMediaPlayer.stop()คุณสามารถระบุได้MediaPlayer.playToggle()ว่าจะสลับระหว่าง "หยุด" และ "กำลังเล่น" วิธีนี้เป็นวิธีที่ปลอดภัยเสมอในการโทร - ไม่มีความเสี่ยงในการเข้าสู่สถานะที่ผิดกฎหมาย

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

  • ในทางปฏิบัติเกี่ยวกับมันและไม่โยนข้อผิดพลาด การทำเช่นนี้จะช่วยลดความยุ่งยากของรหัสได้อย่างมากเช่นแทนที่จะต้องเขียนif (list.contains(x)) { list.remove(x) }คุณต้องการเท่านั้นlist.remove(x)) แต่มันยังสามารถซ่อนข้อบกพร่อง
  • หรือคุณอาจเป็นคนอวดรู้และส่งสัญญาณข้อผิดพลาดว่ารายการไม่มีองค์ประกอบนั้น รหัสอาจมีความซับซ้อนมากขึ้นในบางครั้งเนื่องจากคุณต้องตรวจสอบก่อนว่าเงื่อนไขเป็นที่พอใจก่อนที่จะเรียกวิธีการหรือไม่ แต่ข้อเสียคือข้อบกพร่องในที่สุดนั้นง่ายต่อการติดตาม

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


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

ฉันไม่เคยพูดว่าใช้ง่ายกว่า - การอ้างสิทธิ์ของฉันคือปลอดภัยกว่า นอกจากนี้ตัวอย่างของคุณสมมติว่าผู้ใช้จะได้รับปุ่มหยุดและเล่นแยกต่างหาก หากเป็นเช่นนั้นจริง ๆ แล้วใช่ a playToggleจะยุ่งยากมากที่จะใช้ แต่ถ้าผู้ใช้ยังได้รับปุ่มสลับแทนก็playToggleจะใช้งานง่ายกว่าการเล่น / หยุด
Pedro Rodrigues
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.