จำเป็นต้องเพิ่มตัวพิมพ์เริ่มต้นในขณะที่ใช้ตัวเรือนสวิตช์หรือไม่


41

ในระหว่างการตรวจสอบโค้ดเมื่อเร็ว ๆ นี้ฉันถูกขอให้ใส่defaultเคสในไฟล์ทั้งหมดทุกที่ที่switchบล็อกถูกใช้แม้ว่าจะไม่มีอะไรทำdefaultก็ตาม นั่นหมายความว่าฉันต้องใส่defaultเคสและไม่เขียนอะไรเลย

นี่เป็นสิ่งที่ถูกต้องหรือไม่ มันจะมีจุดประสงค์อะไร


9
มันขึ้นอยู่กับคุณ ... ฉันหมายถึงสไตล์โค้ดของหัวหน้างาน
SD

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

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

คำตอบ:


31

ดูเหมือนว่ามีสามกรณีที่defaultไม่จำเป็นต้องมีคำสั่ง:

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

  2. คุณรู้ว่าswitch caseจะใช้อย่างไรและที่ไหนและจะป้อนค่าใด อีกครั้งสิ่งนี้อาจเปลี่ยนแปลงและอาจจำเป็นต้องมีการประมวลผลเพิ่มเติม

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

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


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

11
กรณีเริ่มต้นมักเป็นความคิดที่ดีเสมอตัวอย่างเช่นในกรณีของ enum หากมีคนเพิ่มรายการใหม่ให้กับ enum กรณีเริ่มต้นของคุณควรทำสิ่งที่คล้ายกับสิ่งthrow new IllegalStateException("Unrecognised case "+myEnum)ที่แสดงให้คุณทราบว่าเกิดอะไรขึ้น
Rich

นี่หมายความว่าคุณควรมีelseประโยคตลอดif/else ifแม้ว่าคุณจะรู้ว่าไม่ควรเกิดขึ้น? ดูเหมือนจะไม่เป็นความคิดที่ดีสำหรับฉัน ...
jadkik94

2
ถ้าฉันอาจเพิ่มเรื่องเล็ก ๆ น้อยจากงานของฉัน: ฉันมีวิธีการจากโรงงานที่ใช้ enum เพื่อยกตัวอย่างบางคลาส ฉันได้ทิ้งคำแนะนำไว้ว่าเมื่อใดก็ตามที่คุณเพิ่มคลาสใหม่ให้กับโปรเจ็กต์นั้นคุณต้องเพิ่มค่าให้กับ enum และเคส o สวิตช์ แน่นอนหนึ่งสัปดาห์หลังจากที่ฉันปิดรหัสมันจะหยุดทำงานโดยมีข้อยกเว้นข้อใดข้อหนึ่งที่ต้องไม่ยกขึ้น ฉันไปที่โปรแกรมเมอร์และถามเขาว่า: "คุณเพิ่มเคสไปที่สวิตช์หรือไม่?" และแน่นอนว่าเขาไม่พอ ในวันนั้นฉันเปลี่ยนข้อยกเว้นเพื่อพูดว่า "ถ้าคุณได้รับข้อความนี้ให้โทษนักพัฒนาคนสุดท้ายที่แตะรหัส"
Ziv

28

นี่เป็นสิ่งที่ถูกต้องใช่ไหม มันจะมีจุดประสงค์อะไร

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

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

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


1
มันคือassertอะไร
FLY

1
@FLY เป็นคำหลักใน Java และมักเป็นแมโครใน C, C ++ เป็นต้นทั้งสองวิธี assert ใช้เพื่อทดสอบว่าข้อสันนิษฐานบางอย่างยังคงเป็นจริงและมีข้อผิดพลาดหรือทำให้เกิดข้อผิดพลาดหากไม่ใช่
แม็กเคเล็บ

ฉันได้แก้ไขความคิดเห็นก่อนหน้าของฉันพร้อมลิงก์ไปยังวิกิพีเดีย
FLY

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

12

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


1
+1 สำหรับการจับปัญหา ณ เวลารวบรวมและไม่ใช่เวลาทำงาน
SylvainD

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

ฉันไม่รู้เกี่ยวกับพฤติกรรมนี้ รหัส ideone ตัวอย่างของฉันideone.com/WvytWqเห็นเป็นอย่างอื่น สมมติว่าคุณพูดว่าเป็นจริงจะเกิดอะไรขึ้นถ้ามีคนเพิ่มค่าให้กับประเภทการแจงนับ แต่คุณไม่ต้องคอมไพล์โค้ดใหม่
emory


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

9

ในหลายประการคำถามนี้เป็นเช่นเดียวกับมักจะถามว่าฉันจะต้องelseเป็นไปตามข้อในตอนท้ายของนั้นif/else ifบันไดที่ครอบคลุมทุกตัวเลือก

คำตอบคือการพูดทางวากยสัมพันธ์ไม่มีคุณไม่ได้ แต่มี ...

defaultประโยคสามารถจะมีสำหรับ (อย่างน้อย) เหตุผลสองประการคือ

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

ปรัชญาของฉันค่อนข้างเรียบง่าย - ประเมินสถานการณ์กรณีที่เลวร้ายที่สุดของสองตัวเลือกและดำเนินการอย่างปลอดภัยที่สุด ในกรณีที่ว่างเปล่าelseหรือdefaultส่วนคำสั่งตัวเลือกกรณีที่แย่ที่สุดคือ:

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

Overdramatic? อาจจะ ... แต่แล้วซอฟต์แวร์ของฉันก็มีโอกาสที่จะฆ่าผู้คนถ้ามันผิดพลาด ฉันไม่อยากเสี่ยง


นอกจากนี้แนวทางMISRA-C {ดูโปรไฟล์สำหรับการติดต่อ} แนะนำdefaultข้อสำหรับทุก ๆ ข้อswitch


ไม่เพียง แต่ในตอนท้ายของบันได คำถามคือผมไม่จำเป็นต้องหลังจากที่else ifแต่อย่างที่คุณพูดไม่มันไม่ใช่
ott--

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

จุดดี @emory - ที่ไม่ชัดเจนมากมันเป็น ... แก้ไข :)
แอนดรู

6

Java ไม่ได้บังคับให้คุณมีคำสั่ง 'เริ่มต้น' แต่เป็นวิธีที่ดีที่จะมีหนึ่งตลอดเวลาแม้ว่ารหัสอาจจะไม่สามารถเข้าถึงได้ (ตอนนี้) นี่คือเหตุผลไม่กี่:

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

เมื่อต้องการรับค่าที่ไม่คาดคิด (ในกรณีที่คุณไม่ได้เปิด enum) ที่ส่งผ่านอาจเป็นค่าที่มากกว่าหรือน้อยกว่าที่คุณคาดไว้

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

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


2

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


0

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

หากคุณแน่ใจจริงๆว่าคุณไม่สนใจกรณีอื่น ๆ ค่าเริ่มต้น: break; // ไม่สนใจ แต่ค่าเริ่มต้นควรเป็นค่าเริ่มต้นเช่นโยนข้อผิดพลาดใหม่ ("ค่าที่ไม่คาดคิด");

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


-2

พิจารณา

enum Gender
{
       MALE ,
       FEMALE ;
}

และ

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

ฉันจะโต้แย้งว่านี่ดีกว่าการมีกรณีเพศชาย, คดีหญิงและกรณีเริ่มต้นที่ส่งข้อยกเว้น

ในระหว่างขั้นตอนการทดสอบคอมพิวเตอร์จะตรวจสอบว่า g เป็นหญิงหรือไม่ ในระหว่างการผลิตการตรวจสอบจะถูกละเว้น (ใช่ microoptimization!)

ที่สำคัญคุณจะต้องรักษาเป้าหมายของคุณให้ครอบคลุมรหัส 100% หากคุณเขียนกรณีเริ่มต้นที่ส่งข้อยกเว้นคุณจะทดสอบได้อย่างไร


1
1. ไม่ต้องการ microoptimalization - การกำจัดรหัสที่ตายแล้วถูกใช้ในคอมไพเลอร์ตลอด 30 ปีที่ผ่านมาและจะถูกกำจัดโดย JIT 2. ความครอบคลุมโค้ด 100% มักไม่ถือว่าสำคัญ (ในมือข้างหนึ่งความครอบคลุม 100% อาจไม่ครอบคลุมกรณีขอบทั้งหมดในอีกกรณีหนึ่งไม่มีการทดสอบจะจับ assert_not_reached บรรทัดในโปรแกรมที่ถูกต้อง) ในกรณีนี้การไม่ใช้ตัวพิมพ์เล็กจะทำให้เกิดข้อผิดพลาดของคอมไพเลอร์ ในอีกทางหนึ่ง - คุณจะตรวจสอบว่า assert ใช้งานได้อย่างไร (คุณเปลี่ยนปัญหาและซ่อนไว้โดยครอบคลุม 100% แทนที่จะมีบรรทัดเดียวกัน 'จะไม่เกิดขึ้นฉันสัญญา')
Maciej Piechotka

1
3. เมื่อไหร่ที่จะGender . FEMALE . equals ( FEMALE ) ;ประเมินfalse? คุณหมายถึงGender.FEMALE.equals (g)แต่เนื่องจากคุณสามารถอ้างอิงถึง enums ที่มีความเสถียรคุณจึงสามารถเขียนg == Gender.FEMALEได้ 4. (ความเห็นส่วนตัว) รหัสดังกล่าวนั้นยากต่อการอ่านและจะทำให้เกิดข้อผิดพลาดที่สับสนเล็กน้อย
Maciej Piechotka

@MaciejPiechotka จับที่ดีเกี่ยวกับ g ใช่ microoptimization เป็น nonbenefit แต่ก็ไม่ใช่ข้อเสียเปรียบ การครอบคลุมโค้ด 100% เป็นประโยชน์ถ้า if เป็นหนึ่งในเป้าหมายของคุณและเครื่องมือการครอบคลุมโค้ดของคุณไม่ได้ตรวจสอบการยืนยัน (ซึ่งไม่ควรมี)
emory

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