ฉันควรกลับจากฟังก์ชันก่อนหน้าหรือใช้คำสั่ง if [ปิด]


304

ฉันมักจะเขียนฟังก์ชันประเภทนี้ทั้งสองรูปแบบและฉันสงสัยว่ารูปแบบใดรูปแบบหนึ่งที่ต้องการมากกว่าอีกรูปแบบหนึ่งและทำไม

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

หรือ

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

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


9
ฉันมาสายสำหรับการสนทนานี้ดังนั้นฉันจะไม่ตอบคำถามนี้ ฉันยังคิดเกี่ยวกับเรื่องนี้เมื่อสองปีที่แล้ว: lecterror.com/articles/view/code-formatting-and-readabilityฉันพบว่าการอ่านแก้ไขและบำรุงรักษาที่สองง่ายขึ้น แต่บางทีนั่นอาจจะเป็นเพียงฉัน :)
ดรฮันนิบาลเล็คเตอร์


4
ตอนนี้คำถามนี้เป็นตัวอย่างที่ดีของคำถามตามความคิดเห็น
Rudolf Olah

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

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

คำตอบ:


402

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


7
Smalltalk เรียก "คำสั่งป้องกัน" เหล่านี้ อย่างน้อยนั่นก็คือสิ่งที่ Kent Beck เรียกพวกเขาในรูปแบบการปฏิบัติที่ดีที่สุดของ Smalltalk ฉันไม่รู้ว่ามันเป็นสำนวนทั่วไปหรือไม่
Frank Shearar

154
การออกก่อนกำหนดจะช่วยให้คุณสามารถนำสิ่งต่าง ๆ ออกจากกองจิต จำกัด :)
Joren

50
ฉันเคยได้ยินสิ่งนี้เรียกว่า "รูปแบบ Bouncer" - กำจัดกรณีที่ไม่ดีก่อนที่พวกเขาจะเข้าประตู
RevBingo

14
ฉันจะไปให้ไกลและพูดอย่างนี้เป็นสิ่งที่ถูกต้องเท่านั้น
Oliver Weiler

38
นอกจากนี้คุณไม่ต้องเพิ่มการเยื้องถ้าคุณกำจัดเส้นขอบกรณีที่จุดเริ่มต้น
doppelgreener

170

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

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

สามารถอ่านได้มากกว่า

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

ฉันยอมรับอย่างเต็มที่ว่าฉันไม่เคยเข้าใจถึงข้อดีของจุดออกเดียว


20
ข้อดีของจุดทางออกเดียวคือ ... มีจุดทางออกเดียว! ด้วยตัวอย่างของคุณมีหลายจุดที่สามารถกลับมาได้ ด้วยฟังก์ชั่นที่ซับซ้อนมากขึ้นซึ่งสามารถเปลี่ยนเป็นจุดล่า - ออก - จุดเมื่อรูปแบบของการคืนค่าเปลี่ยนแปลง แน่นอนว่ามีบางครั้งที่การบังคับให้จุดออกเดียวไม่สมเหตุสมผล
JohnL

71
@JohnL ฟังก์ชั่นใหญ่เป็นปัญหาไม่ใช่ทางออกหลายจุด ยกเว้นว่าคุณกำลังทำงานในบริบทที่การเรียกใช้ฟังก์ชันเพิ่มเติมจะทำให้โค้ดของคุณช้าลงอย่างมาก ...
Dan Rosenstark

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

76
@ Jason Viers ราวกับว่าเป็นคนเดียวที่return value;ช่วย!?! ถ้าอย่างนั้นคุณต้องล่าครึ่งโหลvalue = ...ด้วยข้อเสียที่คุณไม่แน่ใจว่าค่าจะไม่เปลี่ยนแปลงระหว่างการมอบหมายนี้และผลตอบแทนสุดท้าย อย่างน้อยผลตอบแทนทันทีนั้นชัดเจนว่าไม่มีอะไรจะเปลี่ยนแปลงผลลัพธ์อีกต่อไป
Sjoerd

5
@Sjoed: ฉันสอง Sjoerd ที่นี่ หากคุณต้องการบันทึกผลลัพธ์คุณสามารถเข้าสู่ระบบที่ไซต์ผู้โทร หากคุณต้องการทราบเหตุผลคุณต้องเข้าสู่ระบบที่จุดออก / การกำหนดแต่ละจุดเพื่อให้ทั้งคู่เหมือนกันในด้านนี้
Matthieu M.

32

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

ที่กล่าวว่าแม้ว่าหากฟังก์ชั่นต้องการข้อมูลบางอย่างเมื่อมีการเรียกฉันมักจะโยนข้อยกเว้น (ดูตัวอย่าง) เมื่อเทียบกับที่เพิ่งกลับมา

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}

9
และถ้าผลลัพธ์สุดท้ายสามารถรักษาได้ใครจะเลือกสไตล์ไหน
Jeff Siver

3
@Jeff Siver - เหตุใดจึงเป็นเช่นนี้คำถามสไตล์ "สงครามศักดิ์สิทธิ์" ในตอนท้ายของวันมันลงมาเพื่อการตั้งค่าส่วนตัวและสิ่งที่คู่มือสไตล์ภายในกล่าวว่า
rjzii

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

ความซับซ้อนของวงจรที่คอมไพเลอร์ของฉันแนะนำคืออะไร? มันจะดีกว่าหรือไม่ที่จะไม่ซ้อนโค้ดถ้ามันสามารถช่วยได้?
l

24

ฉันชอบผลตอบแทนเร็ว

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

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


13

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

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

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

C # ฉันไม่สามารถตอบได้อย่างแน่นอน

D-language ให้การปกป้องขอบเขตการออกในตัวที่เหมาะสมและพร้อมสำหรับการออกก่อนกำหนดดังนั้นจึงไม่ควรแสดงปัญหาอื่นนอกเหนือจากรูปแบบ

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


1
C ++ อนุญาตให้สิ่งที่เรียกว่า optmization ส่งคืนค่าซึ่งอนุญาตให้คอมไพเลอร์ละเว้นการดำเนินการคัดลอกที่จะเกิดขึ้นตามปกติเมื่อคุณส่งคืนค่า อย่างไรก็ตามภายใต้เงื่อนไขต่าง ๆ ที่ทำได้ยากและค่าส่งคืนหลายรายการเป็นหนึ่งในกรณีเหล่านั้น กล่าวอีกนัยหนึ่งการใช้ค่าส่งคืนหลายค่าใน C ++ อาจทำให้โค้ดของคุณช้าลงได้ สิ่งนี้ถือเป็น MSC อย่างแน่นอนและหากว่าเป็นเรื่องที่ค่อนข้างยุ่งยาก (หากไม่สามารถทำได้) ในการติดตั้ง RVO ที่มีค่าตอบแทนที่เป็นไปได้จำนวนมากมันอาจเป็นปัญหาในคอมไพเลอร์ทั้งหมด
Eamon Nerbonne

1
ใน C เพียงแค่ใช้gotoและอาจเป็นผลตอบแทนสองจุด ตัวอย่าง (การจัดรูปแบบรหัสเป็นไปไม่ได้ในความคิดเห็น):foo() { init(); if (bad) goto err; bar(); if (bad) goto err; baz(); return 0; err: cleanup(); return 1; }
mirabilos

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

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

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

9

ผลตอบแทนก่อนการชนะ พวกเขาสามารถดูน่าเกลียด แต่น่าเกลียดน้อยกว่าifห่อใหญ่โดยเฉพาะอย่างยิ่งถ้ามีหลายเงื่อนไขให้ตรวจสอบ


9

ฉันใช้ทั้งสองอย่าง

หากDoSomethingเป็นโค้ด 3-5 บรรทัดโค้ดจะดูสวยงามโดยใช้วิธีการจัดรูปแบบแรก

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


2
สวยไม่มีทาง! เยื้องมากเกินไป!
JimmyKane

@ JimmyKane มีเพียงการเยื้องมากที่สามารถเกิดขึ้นได้ใน 3-5 บรรทัดโดยเฉพาะอย่างยิ่งเมื่อคุณต้องการ 2 (?) บรรทัดต่อระดับที่เหลือจะได้รับการเยื้อง: หนึ่งสำหรับโครงสร้างการควบคุมและจุดเริ่มต้นของบล็อกหนึ่งสำหรับปลายบล็อก
Deduplicator

8

เหตุผลคลาสสิกสำหรับ single-entry-single-exit คือมิฉะนั้นความหมายที่เป็นทางการจะกลายเป็นสิ่งที่น่าเกลียดอย่างไม่น่าเชื่อ (เหตุผลเดียวกัน GOTO ถือว่าเป็นอันตราย)

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

โดยปกติฉันจะย่อเล็กสุดวิธีคืนต้น


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

@ Tim ขึ้นอยู่กับว่าคุณต้องการทำสิ่งที่คุณคิดและวิเคราะห์ ฉันพบว่า SESE อ่านได้ค่อนข้างดีหาก coder มีสติเกี่ยวกับสิ่งต่าง ๆ
พอลนาธาน

ทัศนคติของฉันในการวิเคราะห์นั้นมาจากการปรับแต่งจากโครงการ Self การเรียกใช้วิธีการแบบไดนามิก 99% สามารถแก้ไขและกำจัดแบบคงที่ได้ ดังนั้นคุณสามารถวิเคราะห์โค้ดเส้นตรงได้ ในฐานะโปรแกรมเมอร์ที่ใช้งานได้ฉันสามารถสันนิษฐานได้ว่าโค้ดส่วนใหญ่ที่ฉันจะทำงานนั้นเขียนโดยโปรแกรมเมอร์ทั่วไป ดังนั้นพวกเขาจะไม่เขียนโค้ดที่ดีมาก
ทิม Williscroft

@Tim: ยุติธรรมเพียงพอ แม้ว่าฉันจะรู้สึกว่าถูกชี้ให้เห็นว่าภาษาคงที่ยังคงมีการเล่นจำนวนมาก
พอลนาธาน

เหตุผลที่ไม่ได้รับการยกเว้น ซึ่งเป็นสาเหตุที่ทางออกเดียวยอดเยี่ยมใน C แต่ไม่ซื้ออะไรเลยใน C ++
peterchen

7

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


6

มันขึ้นอยู่กับ.

กลับมาก่อนถ้ามีเงื่อนไขบางอย่างที่ชัดเจนสำหรับการตรวจสอบว่าจะทำให้ส่วนที่เหลือของฟังก์ชั่นนั้นไม่มีจุดหมาย *

ตั้งค่า Retval + ผลตอบแทนเดียวหากฟังก์ชั่นมีความซับซ้อนมากขึ้นและอาจมีจุดทางออกหลายจุดเป็นอย่างอื่น (ปัญหาการอ่านได้)

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


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

@TMN: จุดดี
Bobby Tables

2
จุดสำคัญที่นี่ก็คือว่าใน OO คุณโยนยกเว้นไม่กลับ การส่งคืนหลายรายการอาจไม่ดีข้อยกเว้นหลายครั้งไม่จำเป็นต้องเป็นกลิ่นรหัส
Michael K

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

3

ใช้ถ้า

ในหนังสือของ Don Knuth เกี่ยวกับ GOTO's ฉันอ่านเขาให้เหตุผลว่าการมีสภาพที่เป็นไปได้มากที่สุดมาก่อนเสมอในประโยค if ภายใต้สมมติฐานที่ว่านี้ยังคงเป็นความคิดที่สมเหตุสมผล (และไม่ใช่หนึ่งในการพิจารณาที่บริสุทธิ์สำหรับความเร็วของยุคนั้น) ฉันจะบอกว่าผลตอบแทนในช่วงต้นนั้นไม่ใช่วิธีการเขียนโปรแกรมที่ดีโดยเฉพาะอย่างยิ่งเมื่อพิจารณาถึงความจริงที่ว่าพวกเขาใช้บ่อยกว่าที่ไม่ได้ใช้ในการจัดการข้อผิดพลาดเว้นแต่ว่าโค้ดของคุณมีแนวโน้มที่จะล้มเหลวมากกว่าไม่ล้มเหลว :-)

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

เฉพาะ Delphi ...

ฉันคิดว่านี่เป็นวิธีการเขียนโปรแกรมที่ดีสำหรับโปรแกรมเมอร์ Delphi แม้ว่าฉันจะไม่มีข้อพิสูจน์ใด ๆ Pre-D2009 เราไม่มีวิธีปรมาณูที่จะคืนค่าเรามีexit;และresult := foo;หรือเราอาจโยนข้อยกเว้น

หากคุณต้องเปลี่ยน

if (true) {
 return foo;
} 

สำหรับ

if true then 
begin
  result := foo; 
  exit; 
end;

คุณอาจเบื่อที่จะเห็นสิ่งนั้นที่อยู่เหนือสุดในทุกฟังก์ชั่นของคุณและชอบ

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

และเพียงหลีกเลี่ยงexitทั้งหมด


2
ด้วย Delphis ใหม่ก็อาจจะยากที่จะเป็นif true then Exit(foo);ผมใช้บ่อยเทคนิคแรกเริ่มต้นresultไปnilหรือFALSEตามลำดับจากนั้นตรวจสอบทุกเงื่อนไขข้อผิดพลาดและเพียงแค่Exit;หากมีการพบกัน กรณีที่ประสบความสำเร็จresultคือ (ปกติ) ตั้งค่าสถานที่ที่ส่วนท้ายของวิธีการ
JensG

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

และโปรแกรมเมอร์ C # ใช่เพื่อความซื่อสัตย์ฉันจะพบว่ามีประโยชน์จริง ๆ เพราะจะช่วยลดจำนวนบรรทัดระหว่างการประกาศและการใช้ (IIRC มีแม้กระทั่งตัวชี้วัดบางอย่างสำหรับการที่ลืมชื่อ
JensG

2

ฉันเห็นด้วยกับข้อความต่อไปนี้:

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

ที่นำมาจากคำถามนี้ใน StackOverflow


+1 สำหรับคำสั่งป้องกัน ฉันยังต้องการยามเชิงที่มุ่งเน้นในเชิงบวกเช่น: หาก (condition = false) ผลตอบแทนตรงข้ามกับ if (! condition) return
JustinC

1

ฉันใช้ผลตอบแทนในช่วงต้นเกือบจะทุกวันนี้ ฉันเขียนสิ่งนี้

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

เช่น

self = [super init];
if (!self)
    return;

// your code here

return self;

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


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

1

ฉันต้องการเขียน:

if(someCondition)
{
    SomeFunction();
}

2
eww การตรวจสอบล่วงหน้านั้นใช่หรือไม่ หรือมันอยู่ใน mehtod เฉพาะการตรวจสอบDoSomeFunctionIfSomeCondition?
STW

สิ่งนี้ทำให้คุณกังวลแยกจากกันอย่างไร?
justkt

2
คุณกำลังละเมิด encapsulation โดยทำให้การใช้งานของฟังก์ชั่น (ตรรกะการพึ่งพาของมัน) ภายนอก
TMN

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

2
นี่คือสไตล์ที่ Robert C. Martin เสนอใน "Clean Code" ฟังก์ชั่นควรทำสิ่งเดียวเท่านั้น อย่างไรก็ตามใน "รูปแบบการนำไปปฏิบัติ" เคนเบ็คแนะนำว่าจากตัวเลือกทั้งสองที่เสนอใน OP ตัวเลือกที่สองนั้นดีกว่า
Scott Whitlock

1

เช่นเดียวกับคุณฉันมักจะเขียนคนแรก แต่ชอบคนสุดท้าย ถ้าฉันมีการตรวจสอบซ้อนกันมากฉันมักจะ refactor วิธีที่สอง

ฉันไม่ชอบว่าการจัดการข้อผิดพลาดถูกย้ายออกไปจากเช็คอย่างไร

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

ฉันชอบสิ่งนี้:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something

0

เงื่อนไขด้านบนเรียกว่า "เงื่อนไข" โดยการใส่if(!precond) return;คุณจะเห็นรายการเงื่อนไขทั้งหมด

การใช้บล็อก "if-else" ขนาดใหญ่อาจเพิ่มค่าใช้จ่ายการเยื้อง


2
อะไร? คุณไม่สามารถส่งคืนต้นได้ใน Java? C #, VB (. NET และ 6) และเห็นได้ชัดว่า Java (ซึ่งฉันคิดว่าทำ แต่ต้องค้นหาตั้งแต่ฉันไม่ได้ใช้ภาษาใน 15 ปี) ทั้งหมดอนุญาตให้ส่งกลับก่อน ดังนั้นอย่ากล่าวโทษ 'ภาษาที่พิมพ์อย่างรุนแรง' เนื่องจากไม่มีคุณสมบัติ stackoverflow.com/questions/884429/…
ps2goat

-1

ฉันชอบที่จะรักษาถ้างบน้อย

ดังนั้นเลือกระหว่าง:

if condition:
   line1
   line2
    ...
   line-n

และ

if not condition: return

line1
line2
 ...
line-n

ฉันจะเลือกสิ่งที่คุณอธิบายว่าเป็น "ผลตอบแทนเร็ว"

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

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


-2

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


-2

ฉันฝึกปฏิบัติไม่สำเร็จในระดับฟังก์ชั่น มันทำให้รหัสสอดคล้องและสะอาด (สำหรับฉันและคนที่ฉันเคยทำงานด้วย) เพราะการที่ฉันกลับก่อนเสมอ

สำหรับเงื่อนไขที่มีการตรวจสอบบางส่วนคุณสามารถใช้แง่มุมต่างๆสำหรับการตรวจสอบเหล่านั้นหากคุณใช้ AOP

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