ฉันเคยได้ยินเสมอเกี่ยวกับฟังก์ชันจุดออกเดี่ยวว่าเป็นวิธีที่ไม่ดีในการเขียนโค้ดเนื่องจากคุณสูญเสียความสามารถในการอ่านและประสิทธิภาพ ฉันไม่เคยได้ยินใครเถียงอีกด้าน
ฉันคิดว่าสิ่งนี้เกี่ยวข้องกับ CS แต่คำถามนี้ถูกยิงที่ cstheory stackexchange
ฉันเคยได้ยินเสมอเกี่ยวกับฟังก์ชันจุดออกเดี่ยวว่าเป็นวิธีที่ไม่ดีในการเขียนโค้ดเนื่องจากคุณสูญเสียความสามารถในการอ่านและประสิทธิภาพ ฉันไม่เคยได้ยินใครเถียงอีกด้าน
ฉันคิดว่าสิ่งนี้เกี่ยวข้องกับ CS แต่คำถามนี้ถูกยิงที่ cstheory stackexchange
คำตอบ:
มีสำนักคิดที่แตกต่างกันและส่วนใหญ่ขึ้นอยู่กับความชอบส่วนบุคคล
อย่างหนึ่งคือมันจะสับสนน้อยกว่าหากมีทางออกเพียงจุดเดียว - คุณมีเส้นทางเดียวผ่านวิธีการและคุณรู้ว่าจะมองหาทางออกที่ไหน ในด้านลบหากคุณใช้การเยื้องเพื่อแสดงการซ้อนโค้ดของคุณจะสิ้นสุดลงด้วยการเยื้องไปทางขวาอย่างหนาแน่นและยากที่จะติดตามขอบเขตที่ซ้อนกันทั้งหมด
อีกประการหนึ่งคือคุณสามารถตรวจสอบเงื่อนไขเบื้องต้นและออกก่อนเวลาเริ่มต้นของวิธีการเพื่อให้คุณทราบในเนื้อความของวิธีการว่าเงื่อนไขบางอย่างเป็นจริงโดยที่เนื้อความทั้งหมดของวิธีการจะไม่เยื้องไปทางขวา 5 ไมล์ โดยปกติจะช่วยลดจำนวนขอบเขตที่คุณต้องกังวลซึ่งทำให้โค้ดติดตามได้ง่ายขึ้นมาก
อย่างที่สามคือคุณสามารถออกจากที่ใดก็ได้ที่คุณต้องการ สิ่งนี้เคยสับสนมากขึ้นในสมัยก่อน แต่ตอนนี้เรามีตัวแก้ไขสีไวยากรณ์และคอมไพเลอร์ที่ตรวจจับโค้ดที่ไม่สามารถเข้าถึงได้มันง่ายกว่ามากในการจัดการ
ฉันอยู่ในค่ายกลาง การบังคับใช้จุดออกเพียงจุดเดียวเป็นข้อ จำกัด IMHO ที่ไม่มีจุดหมายหรือแม้แต่การต่อต้านในขณะที่การออกโดยการสุ่มทั่วทุกวิธีบางครั้งอาจทำให้เกิดความยุ่งเหยิงในการปฏิบัติตามตรรกะซึ่งเป็นการยากที่จะดูว่าโค้ดที่กำหนดจะเป็นหรือไม่ ดำเนินการ แต่การ "gating" วิธีการของคุณทำให้ง่ายขึ้นอย่างมีนัยสำคัญ
singe exit
กระบวนทัศน์โดยการใช้go to
คำพูดเล็กน้อย นอกจากนี้เรายังได้รับโอกาสในการประมวลผลภายหลังบางอย่างภายใต้Error
ฉลากภายในของฟังก์ชันซึ่งเป็นไปไม่ได้ที่จะมีหลายreturn
s
คำแนะนำทั่วไปของฉันคือคำสั่งส่งคืนในทางปฏิบัติควรอยู่ก่อนรหัสแรกที่มีผลข้างเคียงใด ๆ หรือหลังรหัสสุดท้ายที่มีผลข้างเคียงใด ๆ ฉันจะพิจารณาบางสิ่งเช่น:
if (! อาร์กิวเมนต์) // ตรวจสอบว่าไม่ใช่ค่าว่าง ส่งคืน ERR_NULL_ARGUMENT; ... ประมวลผลอาร์กิวเมนต์ที่ไม่ใช่ค่าว่าง ถ้า (ตกลง) กลับ 0; อื่น ส่งคืน ERR_NOT_OK;
ชัดเจนกว่า:
int return_value; if (อาร์กิวเมนต์) // ไม่ใช่ null { .. ประมวลผลอาร์กิวเมนต์ที่ไม่ใช่ค่าว่าง .. กำหนดผลลัพธ์ให้เหมาะสม } อื่น ผลลัพธ์ = ERR_NULL_ARGUMENT; ผลตอบแทน;
หากเงื่อนไขบางอย่างควรป้องกันไม่ให้ฟังก์ชันทำอะไรฉันชอบที่จะกลับออกจากฟังก์ชันก่อนกำหนดที่จุดเหนือจุดที่ฟังก์ชันจะทำอะไรก็ได้ เมื่อฟังก์ชันดำเนินการกับผลข้างเคียงแล้วฉันต้องการกลับจากด้านล่างเพื่อให้ชัดเจนว่าต้องจัดการผลข้างเคียงทั้งหมด
ok
ตัวแปรดูเหมือนว่าวิธีการคืนสินค้าเดียวสำหรับฉัน ยิ่งไปกว่านั้นบล็อก if-else สามารถเขียนreturn ok ? 0 : ERR_NOT_OK;
return
จุดเริ่มต้นก่อนโค้ดทั้งหมดซึ่งทำทุกอย่าง สำหรับการใช้ตัว?:
ดำเนินการการเขียนเป็นบรรทัดแยกต่างหากทำให้ IDE หลาย ๆ ตัวสามารถแนบเบรกพอยต์ดีบักกับสถานการณ์ที่ไม่ตกลงได้ง่ายขึ้น BTW ที่จริงที่สำคัญในการ "ออกจากจุดเดียว" โกหกในการทำความเข้าใจว่าสิ่งที่สำคัญคือการที่สำหรับการโทรเฉพาะแต่ละฟังก์ชั่นปกติออกจากจุดนี้เป็นจุดทันทีหลังจากที่โทร โปรแกรมเมอร์ในปัจจุบันยอมรับว่าเป็นเช่นนั้น แต่สิ่งต่างๆไม่ได้เป็นเช่นนั้นเสมอไป ในบางกรณีรหัสที่หายากอาจต้องได้รับโดยไม่มีพื้นที่สแต็กนำไปสู่ฟังก์ชั่น ...
จุดเข้าและออกเดี่ยวเป็นแนวคิดดั้งเดิมของการเขียนโปรแกรมแบบมีโครงสร้างเทียบกับการเข้ารหัสแบบสปาเก็ตตี้ทีละขั้นตอน มีความเชื่อว่าฟังก์ชันจุดออกหลายจุดต้องใช้รหัสมากขึ้นเนื่องจากคุณต้องทำการล้างช่องว่างหน่วยความจำที่จัดสรรให้กับตัวแปรอย่างเหมาะสม พิจารณาสถานการณ์ที่ฟังก์ชันจัดสรรตัวแปร (ทรัพยากร) และการออกจากฟังก์ชันก่อนกำหนดและไม่มีการล้างข้อมูลอย่างเหมาะสมจะทำให้ทรัพยากรรั่วไหล นอกจากนี้การสร้างการล้างข้อมูลก่อนออกทุกครั้งจะสร้างรหัสซ้ำซ้อนจำนวนมาก
โดยส่วนใหญ่แล้วจะขึ้นอยู่กับความต้องการของผู้ส่งมอบ ใน "สมัยก่อน" รหัสสปาเก็ตตี้ที่มีจุดส่งคืนหลายจุดทำให้เกิดการรั่วไหลของหน่วยความจำเนื่องจากตัวเข้ารหัสที่ชอบวิธีนั้นมักจะทำความสะอาดได้ไม่ดี นอกจากนี้ยังมีปัญหากับคอมไพลเลอร์บางตัวที่ "สูญเสีย" การอ้างอิงไปยังตัวแปร return เนื่องจากสแต็กถูกดึงออกมาในระหว่างการส่งคืนในกรณีที่ส่งคืนจากขอบเขตที่ซ้อนกัน ปัญหาทั่วไปคือหนึ่งในโค้ด re-entrant ซึ่งพยายามให้สถานะการเรียกของฟังก์ชันเหมือนกับสถานะการส่งคืน ผู้กลายพันธุ์ของ oop ละเมิดสิ่งนี้และแนวคิดนี้ได้รับการระงับ
มีการส่งมอบเมล็ดที่สะดุดตาที่สุดซึ่งต้องการความเร็วที่จุดออกหลายจุดให้ โดยปกติสภาพแวดล้อมเหล่านี้จะมีหน่วยความจำและการจัดการกระบวนการของตัวเองดังนั้นความเสี่ยงที่จะเกิดการรั่วไหลจะลดลง
โดยส่วนตัวแล้วฉันชอบที่จะมีทางออกเพียงจุดเดียวเนื่องจากฉันมักจะใช้มันเพื่อแทรกเบรกพอยต์ในคำสั่งส่งคืนและทำการตรวจสอบโค้ดว่าโค้ดกำหนดโซลูชันนั้นอย่างไร ฉันสามารถไปที่ทางเข้าและก้าวผ่านซึ่งฉันทำด้วยโซลูชันที่ซ้อนกันและวนซ้ำอย่างกว้างขวาง ในฐานะผู้ตรวจสอบโค้ดผลตอบแทนหลายรายการในฟังก์ชันต้องการการวิเคราะห์ที่ลึกซึ้งยิ่งขึ้นดังนั้นหากคุณกำลังดำเนินการเพื่อเร่งการใช้งานคุณกำลังปล้นปีเตอร์เพื่อช่วยชีวิตพอล จะต้องใช้เวลามากขึ้นในการตรวจสอบโค้ดทำให้ข้อสันนิษฐานของการใช้งานที่มีประสิทธิภาพเป็นโมฆะ
- 2 เซนต์
โปรดดูเอกสารนี้สำหรับรายละเอียดเพิ่มเติม: NISTIR 5459
multiple returns in a function requires a much deeper analysis
เฉพาะในกรณีที่ฟังก์ชั่นมีขนาดใหญ่อยู่แล้ว (> 1 หน้าจอ) มิฉะนั้นจะทำให้การวิเคราะห์ง่ายขึ้น
ในมุมมองของฉันคำแนะนำในการออกจากฟังก์ชัน (หรือโครงสร้างการควบคุมอื่น ๆ ) เพียงจุดเดียวมักจะขายเกิน โดยทั่วไปเหตุผลสองประการคือให้ออกเพียงจุดเดียว:
เหตุผลประการที่สองนั้นละเอียดอ่อนและมีข้อดีบางประการโดยเฉพาะอย่างยิ่งถ้าฟังก์ชันส่งคืนโครงสร้างข้อมูลขนาดใหญ่ อย่างไรก็ตามฉันจะไม่กังวลกับมันมากนักยกเว้น ...
หากเป็นนักเรียนคุณต้องการได้รับคะแนนสูงสุดในชั้นเรียนของคุณ ทำในสิ่งที่ผู้สอนชอบ เขาอาจมีเหตุผลที่ดีจากมุมมองของเขา ดังนั้นอย่างน้อยที่สุดคุณจะได้เรียนรู้มุมมองของเขา สิ่งนี้มีคุณค่าในตัวเอง
โชคดี.
ฉันเคยเป็นผู้สนับสนุนรูปแบบการออกเดี่ยว เหตุผลของฉันส่วนใหญ่มาจากความเจ็บปวด ...
การออกทางเดียวนั้นง่ายกว่าในการแก้ไขข้อบกพร่อง
ด้วยเทคนิคและเครื่องมือที่เรามีในปัจจุบันนี่เป็นตำแหน่งที่สมเหตุสมผลน้อยกว่ามากในการทดสอบหน่วยและการบันทึกอาจทำให้การออกครั้งเดียวไม่จำเป็น ที่กล่าวว่าเมื่อคุณต้องการดูโค้ดรันในดีบักเกอร์การทำความเข้าใจและทำงานกับโค้ดที่มีจุดออกหลายจุดนั้นยากกว่ามาก
สิ่งนี้จะกลายเป็นจริงโดยเฉพาะอย่างยิ่งเมื่อคุณต้องการแทรกการมอบหมายงานเพื่อตรวจสอบสถานะ (แทนที่ด้วยนิพจน์ของนาฬิกาในตัวแก้ไขข้อบกพร่องสมัยใหม่) นอกจากนี้ยังง่ายเกินไปที่จะเปลี่ยนขั้นตอนการควบคุมด้วยวิธีที่ซ่อนปัญหาหรือทำลายการดำเนินการทั้งหมด
วิธีการออกเดี่ยวทำได้ง่ายกว่าในการดีบักเกอร์และง่ายต่อการแยกออกจากกันโดยไม่ทำลายตรรกะ
คำตอบขึ้นอยู่กับบริบทมาก หากคุณกำลังสร้าง GUI และมีฟังก์ชั่นที่เริ่มต้น API และเปิดหน้าต่างเมื่อเริ่มต้นหลักของคุณมันจะเต็มไปด้วยการโทรซึ่งอาจทำให้เกิดข้อผิดพลาดซึ่งแต่ละอย่างจะทำให้อินสแตนซ์ของโปรแกรมปิด หากคุณใช้คำสั่ง IF แบบซ้อนและเยื้องโค้ดของคุณอาจเอียงไปทางขวาอย่างรวดเร็ว การกลับมาของข้อผิดพลาดในแต่ละขั้นตอนอาจจะดีกว่าและสามารถอ่านได้จริงในขณะที่แก้ไขข้อผิดพลาดได้ง่ายด้วยแฟล็กสองสามตัวในโค้ด
อย่างไรก็ตามหากคุณกำลังทดสอบเงื่อนไขที่แตกต่างกันและส่งคืนค่าที่แตกต่างกันโดยขึ้นอยู่กับผลลัพธ์ในวิธีการของคุณอาจเป็นการดีกว่ามากที่จะมีจุดออกเพียงจุดเดียว ฉันเคยทำงานกับสคริปต์การประมวลผลภาพใน MATLAB ซึ่งอาจมีขนาดใหญ่มาก ทางออกหลายจุดอาจทำให้โค้ดติดตามยากมาก คำสั่ง Switch เหมาะสมกว่ามาก
สิ่งที่ดีที่สุดที่ควรทำคือเรียนรู้ไปเรื่อย ๆ หากคุณกำลังเขียนโค้ดสำหรับบางสิ่งให้ลองค้นหาโค้ดของคนอื่นและดูว่าพวกเขานำโค้ดไปใช้อย่างไร ตัดสินใจว่าคุณชอบบิตไหนและไม่ชอบบิตไหน
หากคุณรู้สึกว่าต้องการจุดออกหลายจุดในฟังก์ชันแสดงว่าฟังก์ชันมีขนาดใหญ่เกินไปและทำงานมากเกินไป
ฉันอยากจะแนะนำให้อ่านบทเกี่ยวกับฟังก์ชันในหนังสือ Clean Code ของ Robert C.
โดยพื้นฐานแล้วคุณควรพยายามเขียนฟังก์ชันด้วยโค้ด 4 บรรทัดหรือน้อยกว่า
บันทึกบางส่วนจากบล็อกของ Mike Long :