ฟังก์ชั่นควรมีคำสั่งคืนเดียวหรือไม่?


780

มีเหตุผลที่ดีหรือไม่ที่จะเป็นการดีกว่าที่จะมีคำสั่ง return หนึ่งคำในฟังก์ชัน?

หรือมันโอเคที่จะกลับจากฟังก์ชั่นทันทีที่มันถูกต้องตามหลักเหตุผลในการทำเช่นนั้นความหมายอาจมีหลายคำสั่ง return ในฟังก์ชั่น?


25
ฉันไม่เห็นด้วยว่าคำถามนี้เป็นภาษาที่ไม่เชื่อเรื่องพระเจ้า สำหรับบางภาษาการได้รับผลตอบแทนหลายรายการนั้นเป็นธรรมชาติและสะดวกสบายกว่ากับภาษาอื่น ๆ ฉันมีแนวโน้มที่จะบ่นเกี่ยวกับการคืนค่าในฟังก์ชัน C เร็วกว่า C ++ ที่ใช้ RAII
Adrian McCarthy

3
สิ่งนี้เกี่ยวข้องอย่างใกล้ชิดและมีคำตอบที่ยอดเยี่ยม: programmers.stackexchange.com/questions/118703/…
Tim Schmelter

ภาษาไม่เชื่อเรื่องพระเจ้า? อธิบายให้คนที่ใช้ภาษาที่ใช้งานฟังก์ชั่นที่เขาต้องใช้หนึ่งผลตอบแทนต่อฟังก์ชั่น: p
Boiethios

คำตอบ:


741

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

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

... สามารถทำให้อ่านได้มากขึ้น (IMHO) เช่นนี้

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

ใช่ฉันคิดว่ามันดีที่มี "exit points" หลายอันจาก function / method


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

172
สิ่งนี้เรียกว่า "คำแถลงการณ์ยามยาม" คือการปรับโครงสร้างของฟาวเลอร์
Lars Westergren

12
เมื่อฟังก์ชั่นถูกเก็บไว้ค่อนข้างสั้นมันไม่ยากที่จะติดตามโครงสร้างของฟังก์ชั่นที่มีจุดกลับใกล้กลาง
KJAWolf

21
บล็อกขนาดใหญ่ของคำสั่ง if-else แต่ละรายการมีการส่งคืน? นั่นคืออะไร สิ่งนี้มักจะได้รับการปรับปรุงใหม่ได้ง่าย (อย่างน้อยที่สุดรูปแบบทางออกเดียวที่พบบ่อยมากขึ้นพร้อมกับตัวแปรผลลัพธ์คือดังนั้นฉันสงสัยว่ารูปแบบทางออกหลายทางออกนั้นยากกว่านี้) หากคุณต้องการปวดหัวจริง ๆ ให้ดูที่การรวมกันของ if- อื่นและในขณะที่ลูป (ควบคุมโดยท้องถิ่น booleans) ซึ่งตั้งค่าตัวแปรผลลัพธ์นำไปสู่จุดออกสุดท้ายในตอนท้ายของวิธีการ นั่นเป็นความคิดทางออกเดียวที่บ้าคลั่งและใช่ฉันกำลังพูดจากประสบการณ์จริงที่ต้องจัดการกับมัน
Marcus Andrén

7
'ลองนึกภาพ: คุณต้องทำวิธีการ "IncreStuffCallCounter" ที่ท้ายฟังก์ชั่น' DoStuff ' คุณจะทำอะไรในกรณีนี้ :) '-DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
Jim Balter

355

ไม่มีใครพูดถึงหรืออ้างถึงCode Completeดังนั้นฉันจะทำ

17.1 ผลตอบแทน

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

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


64
+1 สำหรับความแตกต่างของ "ย่อ" แต่ไม่ห้ามการส่งคืนหลายรายการ
Raedwald

13
"ยากที่จะเข้าใจ" เป็นเรื่องส่วนตัวมากโดยเฉพาะอย่างยิ่งเมื่อผู้เขียนไม่ได้ให้หลักฐานเชิงประจักษ์ใด ๆ ที่จะสนับสนุนข้อเรียกร้องทั่วไป ... มีตัวแปรเดียวที่จะต้องตั้งอยู่ในสถานที่ที่มีเงื่อนไขจำนวนมากในรหัสสำหรับคำสั่งส่งคืนสุดท้าย ถึง "ไม่ทราบถึงความเป็นไปได้ที่ตัวแปรได้รับมอบหมายให้อยู่เหนือฟังก์ชัน int!
Heston T. Holtmann

26
โดยส่วนตัวแล้วผมชอบที่จะกลับมาเร็วที่สุดเท่าที่จะทำได้ ทำไม? เมื่อคุณเห็นคำหลักส่งคืนสำหรับกรณีที่กำหนดคุณจะรู้ได้ทันทีว่า "ฉันเสร็จแล้ว" - คุณไม่จำเป็นต้องอ่านต่อเพื่อหาว่าจะเกิดอะไรขึ้นหากมีอะไรเกิดขึ้นภายหลัง
Mark Simpson

12
@ HestonT.Holtmann: สิ่งที่ทำให้Code Completeไม่เหมือนใครในหนังสือเขียนโปรแกรมคือคำแนะนำนั้นได้รับการสนับสนุนจากหลักฐานเชิงประจักษ์
Adrian McCarthy

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

229

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

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

เปรียบเทียบสิ่งนี้กับโค้ดที่อนุญาตให้มีจุดออกหลายจุด: -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

ฉันคิดว่าหลังมีความชัดเจนมาก เท่าที่ฉันสามารถบอกได้ว่าการวิพากษ์วิจารณ์ของจุดออกหลายจุดนั้นเป็นมุมมองที่ค่อนข้างล้าสมัยในทุกวันนี้


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

7
ก่อนอื่นนี่คือตัวอย่างที่วางแผนไว้ ประการที่สองที่ไหน: string ret; "ไปในรุ่นที่สองหรือไม่ประการที่สาม Ret ไม่มีข้อมูลที่เป็นประโยชน์ประการที่สี่ทำไมตรรกะมากในฟังก์ชั่น / วิธีหนึ่งที่ห้าทำไมไม่ทำ DidValuesPass (res res ประเภท) จากนั้นแยก RestOfCode () subfunctions?
Rick Minerich

25
@Rick 1. ไม่ได้มีประสบการณ์ในการทำแบบนี้จริง ๆ แล้วเป็นรูปแบบที่ฉันได้เจอมาหลายครั้ง 2. มันถูกกำหนดใน 'รหัสที่เหลือ' บางทีมันอาจไม่ชัดเจน 3. อืม มันเป็นตัวอย่าง? 4. ฉันเดาว่ามันถูกออกแบบมาในแง่นี้ แต่ก็มีความเป็นไปได้ที่จะพิสูจน์สิ่งนี้ได้มาก 5. สามารถทำได้ ...
ljs

5
@Rick ประเด็นก็คือมันมักจะกลับง่ายกว่าที่จะห่อโค้ดในขนาดใหญ่ถ้าคำสั่ง มันเกิดขึ้นมากมายในประสบการณ์ของฉันแม้จะมีการปรับสภาพที่เหมาะสม
ljs

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

191

ขณะนี้ฉันกำลังทำงานกับ codebase ซึ่งคนสองคนกำลังทำอยู่นั้นสมัครสมาชิกกับทฤษฎี "จุดเดียวของทางออก" และฉันสามารถบอกคุณได้ว่าจากประสบการณ์มันเป็นการปฏิบัติที่น่ากลัวอย่างยิ่ง มันทำให้รหัสยากมากที่จะรักษาและฉันจะแสดงสาเหตุ

ด้วยทฤษฎี "จุดเดียวของทางออก" คุณต้องปิดท้ายด้วยโค้ดที่มีลักษณะดังนี้:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

สิ่งนี้ไม่เพียง แต่จะทำให้โค้ดยากที่จะติดตาม แต่ตอนนี้พูดในภายหลังว่าคุณจำเป็นต้องย้อนกลับไปและเพิ่มการดำเนินการระหว่าง 1 และ 2 คุณต้องเยื้องฟังก์ชัน freaking ทั้งหมดและขอให้โชคดี เงื่อนไข if / else และวงเล็บปีกกาของคุณถูกจับคู่อย่างเหมาะสม

วิธีนี้ทำให้การบำรุงรักษารหัสทำได้ยากมากและเกิดข้อผิดพลาดได้ง่าย


5
@Murph: คุณไม่มีทางรู้ว่าไม่มีอะไรเกิดขึ้นหลังจากแต่ละเงื่อนไขโดยไม่อ่านแต่ละข้อ โดยปกติฉันจะบอกว่าหัวข้อประเภทนี้เป็นเรื่องส่วนตัว แต่นี่เป็นความผิดที่ชัดเจน ด้วยการย้อนกลับแต่ละข้อผิดพลาดคุณก็เสร็จแล้วคุณจะรู้ว่าเกิดอะไรขึ้น
GEOCHET

6
@Murph: ฉันเห็นโค้ดประเภทนี้ถูกใช้งานถูกใช้อย่างไม่ถูกต้องและใช้งานมากเกินไป ตัวอย่างนี้ค่อนข้างง่ายเนื่องจากไม่มีจริงถ้าภายใน / อื่น สิ่งที่รหัสประเภทนี้จำเป็นต้องทำลายคือ "ลืม" อย่างใดอย่างหนึ่ง AFAIK รหัสนี้เป็นข้อยกเว้นที่ไม่ดีอย่างแท้จริง
paercebal

15
คุณสามารถ refactor ลงในสิ่งนี้รักษา "ความบริสุทธิ์" ของมัน: ถ้า (! SUCCEEDED (Operation1 ())) {} else error = OPERATION1FAILED; if (error! = S_OK) {if (SUCCEEDED (Operation2 ())) {} else error = OPERATION2FAILED; } if (error! = S_OK) {if (SUCCEEDED (Operation3 ())) {} else error = OPERATION3FAILED; } // ฯลฯ
Joe Pineda

6
รหัสนี้ไม่ได้มีเพียงหนึ่งจุดออก: แต่ละคำสั่ง "error =" เหล่านั้นอยู่ในเส้นทางออก มันไม่ได้เกี่ยวกับการออกจากฟังก์ชั่นเท่านั้นมันคือการออกจากบล็อกหรือลำดับ
Jay Bazuzi

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

72

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


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

15
"ทำงานได้ดีถ้าคุณทำตามคำแนะนำอื่น ๆ ของเขาและเขียนฟังก์ชันเล็ก ๆ " นี่คือจุดที่สำคัญที่สุด ฟังก์ชั่นขนาดเล็กไม่ค่อยต้องการจุดคืนค่าจำนวนมาก

6
@wnoise +1 สำหรับความคิดเห็นจริง ๆ "การเขียนโปรแกรมโครงสร้าง" ทั้งหมดกล่าวว่าไม่ได้ใช้ GOTO
paxos1977

1
@ceretullis: เว้นแต่เป็นสิ่งจำเป็น แน่นอนว่ามันไม่จำเป็น แต่สามารถใช้ได้เต็มใน C เคอร์เนล linux ใช้มันและด้วยเหตุผลที่ดี GOTO พิจารณาว่ามีอันตรายพูดคุยเกี่ยวกับการใช้GOTOเพื่อย้ายการควบคุมการไหลแม้ว่าจะมีฟังก์ชั่นอยู่ก็ตาม มันไม่เคยบอกว่า "ไม่เคยใช้GOTO"
Esteban Küber

1
"ทั้งหมดเกี่ยวกับการละเว้น 'โครงสร้าง' ของโค้ด" - ไม่ตรงกันข้ามเลย "เหมาะสมแล้วที่จะบอกว่าควรหลีกเลี่ยง" - ไม่ไม่
Jim Balter

62

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

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

ฉันพบฟังก์ชั่นที่เขียนด้วยคำสั่ง Guard ง่ายต่อการติดตามมากกว่าข้อความสั่งที่ซ้อนกันยาวหนึ่งif then elseประโยค


แน่นอน "คำสั่ง if-then-else ที่ซ้อนกันยาว ๆ หนึ่งอัน" ไม่ใช่ทางเลือกเดียวที่จะปกป้องคำสั่ง
Adrian McCarthy

@AdrianMcCarthy คุณมีทางเลือกที่ดีกว่าหรือไม่ มันจะมีประโยชน์มากกว่าการเสียดสี
shinzou

@kuhaku: ฉันไม่แน่ใจว่าฉันจะเรียกว่าการเสียดสี คำตอบแสดงให้เห็นว่ามันเป็นอย่างใดอย่างหนึ่ง / หรือสถานการณ์: ทั้งคำสั่งป้องกันหรือกลุ่มที่ซ้อนกันยาวถ้า - แล้ว - อย่างอื่น ภาษาการเขียนโปรแกรมส่วนใหญ่ (ส่วนใหญ่) เสนอหลายวิธีในการคำนึงถึงตรรกะดังกล่าวนอกเหนือจากคำสั่งป้องกัน
Adrian McCarthy

61

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

กล่าวอีกนัยหนึ่งเมื่อเป็นไปได้โปรดสนับสนุนสไตล์นี้

return a > 0 ?
  positively(a):
  negatively(a);

มากกว่านี้

if (a > 0)
  return positively(a);
else
  return negatively(a);

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

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


6
+1 "หากคุณพบว่าไอเอฟเอสและเอลส์ของคุณอยู่ไกลกันมากทางไวยากรณ์คุณอาจต้องการแบ่งมันออกเป็นฟังก์ชั่นที่เล็กลง"
Andres Jaan Tack

4
+1 หากเป็นปัญหามักจะหมายความว่าคุณทำมากเกินไปในฟังก์ชั่นเดียว มันทำให้ฉันเศร้าใจว่านี่ไม่ใช่คำตอบที่โหวตสูงสุด
แมตต์บริกส์

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

@ MaartenBodewes-owlstead โปรดดู"ความ
Apocalisp

43

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


กล่าวว่าดี แต่ผมไม่เชื่อว่ามันจะดีกว่าที่จะคัดลอกลบเมื่อพยายามที่จะเขียนโค้ดเพิ่มประสิทธิภาพสูง (เช่นซอฟแวร์ที่น่าสนใจตาข่าย 3 มิติที่ซับซ้อน!)
แกรนท์ปีเตอร์ส

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

สิ่งนี้ทำให้เกิดข้อยกเว้นที่น่าสนใจสำหรับกฎ: หากภาษาการเขียนโปรแกรมของคุณไม่มีสิ่งที่ถูกเรียกโดยอัตโนมัติในตอนท้ายของวิธีการ (เช่นtry... finallyใน Java) และคุณจำเป็นต้องทำการบำรุงรักษาทรัพยากรคุณสามารถทำได้เพียงครั้งเดียว กลับมาที่จุดสิ้นสุดของวิธีการ ก่อนที่คุณจะทำสิ่งนี้คุณควรพิจารณาปรับเปลี่ยนรหัสอย่างจริงจังเพื่อกำจัดสถานการณ์
Maarten Bodewes

@PeteKirkham ทำไม goto ถึงทำความสะอาดไม่ดี? ใช่ goto สามารถใช้งานได้ไม่ดี แต่การใช้งานนี้ไม่เลว
q126y

1
@ q126y ใน C ++ ไม่เหมือนกับ RAII มันล้มเหลวเมื่อมีข้อผิดพลาดเกิดขึ้น ใน C มันเป็นวิธีปฏิบัติที่สมบูรณ์แบบ ดูstackoverflow.com/questions/379172/use-goto-or-not
Pete Kirkham

40

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


38

มีเหตุผลที่ดีหรือไม่ที่จะเป็นการดีกว่าที่จะมีคำสั่ง return หนึ่งคำในฟังก์ชัน?

ใช่มี:

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

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

อัปเดต : แนวทาง MISRAเห็นได้ชัดว่าส่งเสริมการออกเพียงครั้งเดียวเช่นกัน

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


2
อีกเหตุผลที่ดีน่าจะเป็นวันที่ดีที่สุดในการมีคำสั่งส่งคืนเดียวคือการบันทึก ถ้าคุณต้องการเพิ่มการบันทึกไปยังวิธีการคุณสามารถวางคำสั่งบันทึกเดียวที่บ่งบอกถึงสิ่งที่วิธีการส่งกลับ
inor

คำสั่ง FORTRAN ENTRY พบได้บ่อยเพียงใด ดูdocs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html และถ้าคุณชอบผจญภัยคุณสามารถบันทึกวิธีการด้วย AOP และคำแนะนำหลังเลิกเรียน
Eric Jablow

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

ฉันต้องการเพิ่มที่มี C # และ Code Contracts ปัญหาโพสต์เงื่อนไขนั้นไม่ใช่เรื่องใหม่เนื่องจากคุณยังสามารถใช้Contract.Ensuresกับจุดส่งคืนหลายจุดได้
julealgon

1
@ q126y: หากคุณใช้gotoเพื่อรับรหัสการล้างข้อมูลทั่วไปคุณอาจทำให้ฟังก์ชั่นนั้นง่ายขึ้นเพื่อให้มีรหัสreturnที่ท้ายรหัสการล้างข้อมูล ดังนั้นคุณอาจจะบอกว่าคุณได้แก้ไขปัญหาด้วยแต่ผมว่าคุณแก้ไขได้โดยง่ายที่จะเป็นหนึ่งเดียวgoto return
Adrian McCarthy

33

การมีจุดออกเพียงจุดเดียวจะให้ประโยชน์ในการดีบักเนื่องจากช่วยให้คุณสามารถตั้งค่าเบรกพอยต์เดียวที่ท้ายฟังก์ชั่นเพื่อดูว่าจะส่งคืนค่าใด


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

3
ใช่. ฉันกำลังเพิ่มรหัสบันทึกลงในระบบที่ใช้งานผิดปกติในการผลิตเป็นระยะ (ซึ่งฉันไม่สามารถผ่านได้) ถ้า coder ก่อนหน้านี้ใช้ทางออกเดียวมันจะง่ายกว่ามาก
Michael Blackburn

3
จริงในการแก้ไขข้อบกพร่องจะเป็นประโยชน์ แต่ในทางปฏิบัติในกรณีส่วนใหญ่ฉันสามารถตั้งค่าเบรกพอยต์ในฟังก์ชั่นการโทรเพียงหลังจากการโทร - ได้ผลอย่างมีประสิทธิภาพเดียวกัน (และตำแหน่งนั้นอยู่บน call stack แน่นอน) YMMV
foo

นี่คือยกเว้นในกรณีที่ดีบักเกอร์ของคุณมีฟังก์ชันการ step-out หรือ step-return (และดีบักเกอร์แต่ละอันจะทำเท่าที่ฉันรู้) ซึ่งแสดงค่าที่ส่งคืนหลังจากส่งคืน การเปลี่ยนค่าในภายหลังอาจเป็นเรื่องยุ่งยากเล็กน้อยหากไม่ได้กำหนดให้กับตัวแปร
Maarten Bodewes

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

19

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


"โดยทั่วไปฉันพยายามที่จะมีเพียงจุดทางออกเดียวจากฟังก์ชั่น" - ทำไม? "เป้าหมายควรเป็นจุดออกน้อยที่สุด" - ทำไม และทำไม 19 คนลงคะแนนไม่ตอบคำถามนี้?
Jim Balter

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

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

14

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

(ค่อนข้างนอกเหนือจากข้อเท็จจริงที่ว่าฟังก์ชั่นหลายสายในภาษาที่มีข้อยกเว้นจะมีจุดออกหลายจุดอยู่แล้ว)


14

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

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

เมื่อเห็นรหัสนี้ฉันจะถามทันที:

  • 'foo' เป็นโมฆะหรือไม่
  • ถ้าเป็นเช่นนั้นลูกค้าจำนวนมากของ 'DoStuff' เคยเรียกใช้ฟังก์ชันที่มีค่า null เป็น 'foo' หรือไม่

อาจขึ้นอยู่กับคำตอบของคำถามเหล่านี้

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

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

มีสองเหตุผลอื่น ๆ (โดยเฉพาะฉันคิดว่ารหัส C ++) ที่มีอยู่หลายรายการจริงอาจมีผลกระทบเชิงลบ ขนาดรหัสและการเพิ่มประสิทธิภาพของคอมไพเลอร์

วัตถุที่ไม่ใช่ POD C ++ ในขอบเขตที่ออกจากฟังก์ชันจะมี destructor ที่เรียกว่า ในกรณีที่มีข้อความสั่งคืนหลายรายการอาจเป็นกรณีที่มีวัตถุต่าง ๆ อยู่ในขอบเขตดังนั้นรายการของ destructors ที่จะเรียกจะแตกต่างกัน คอมไพเลอร์จำเป็นต้องสร้างรหัสสำหรับแต่ละคำสั่งการส่งคืน:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

หากขนาดรหัสเป็นปัญหา - นี่อาจเป็นสิ่งที่ควรหลีกเลี่ยง

ปัญหาอื่น ๆ เกี่ยวข้องกับ "การเพิ่มประสิทธิภาพการคืนค่าชื่อ" (aka Copy Elision, ISO C ++ '03 12.8 / 15) C ++ อนุญาตให้มีการนำไปใช้ข้ามการเรียกตัวสร้างสำเนาหากสามารถ:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

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

จุดทางออกหลายจุดทำให้การทำงานของคอมไพเลอร์ยุ่งยากในการตรวจจับสิ่งนี้และอย่างน้อยสำหรับเวอร์ชันล่าสุดของ VC ++ การปรับให้เหมาะสมไม่ได้เกิดขึ้นที่ส่วนของฟังก์ชั่นมีผลตอบแทนหลายตัว ดูการเพิ่มประสิทธิภาพค่าที่ส่งคืนของชื่อใน Visual C ++ 2005สำหรับรายละเอียดเพิ่มเติม


1
หากคุณนำทั้งหมดยกเว้น dtor สุดท้ายจากตัวอย่าง C ++ ของคุณโค้ดที่จะทำลาย B และ C และ B ในภายหลังจะยังคงต้องถูกสร้างขึ้นเมื่อขอบเขตของคำสั่ง if สิ้นสุดดังนั้นคุณจะไม่ได้รับอะไรเลยโดยไม่มีผลตอบแทนหลายรายการ .
Eclipse

4
+1 และ waaaaay ลงที่ด้านล่างของรายการเรามีเหตุผลที่แท้จริงของการเข้ารหัสนี้มีอยู่ - NRVO อย่างไรก็ตามนี่คือการเพิ่มประสิทธิภาพขนาดเล็ก และเช่นเดียวกับวิธีการเพิ่มประสิทธิภาพแบบไมโครอาจเริ่มต้นโดย "ผู้เชี่ยวชาญ" อายุ 50 ปีที่คุ้นเคยกับการเขียนโปรแกรมใน 300 kHz PDP-8 และไม่เข้าใจความสำคัญของรหัสที่สะอาดและมีโครงสร้าง โดยทั่วไปให้คำแนะนำของ Chris S และใช้คำสั่ง return หลายคำสั่งเมื่อใดก็ตามที่เหมาะสม
BlueRaja - Danny Pflughoeft

ในขณะที่ฉันไม่เห็นด้วยกับความต้องการของคุณ (ในใจของฉันคำแนะนำยืนยันของคุณยังเป็นจุดกลับเช่นเดียวกับthrow new ArgumentNullException()ใน C # ในกรณีนี้) ฉันชอบข้อพิจารณาอื่น ๆ ของคุณพวกเขาทั้งหมดใช้ได้กับฉันและอาจมีความสำคัญในบางอย่าง บริบทเฉพาะ
julealgon

นี่คือหนุนเต็มไปด้วยฟาง คำถามที่ว่าทำไมถึงfooถูกทดสอบไม่มีอะไรเกี่ยวข้องกับเรื่องซึ่งจะทำif (foo == NULL) return; dowork; หรือif (foo != NULL) { dowork; }
Jim Balter

11

การมีจุดทางออกเดียวจะช่วยลดความซับซ้อนของ Cyclomaticดังนั้นในทางทฤษฎีแล้วลดความน่าจะเป็นที่คุณจะแนะนำบั๊กในโค้ดของคุณเมื่อคุณเปลี่ยน อย่างไรก็ตามการฝึกซ้อมมีแนวโน้มที่จะแนะนำว่าจำเป็นต้องมีวิธีการปฏิบัติเพิ่มเติม ฉันมักจะตั้งเป้าหมายที่จะออกจากจุดเดียว แต่ให้รหัสของฉันมีหลายจุดหากอ่านได้มากกว่า


ลึกซึ้งมาก แม้ว่าฉันจะรู้สึกว่าจนกว่าโปรแกรมเมอร์จะรู้ว่าเมื่อใดควรใช้จุดออกหลายจุดพวกเขาควรถูก จำกัด
Rick Minerich

5
ไม่ได้จริงๆ ความซับซ้อนของวัฏจักรของ "if (... ) return; ... return;" เหมือนกับ "if (... ) {... } return;" พวกเขาทั้งสองมีสองเส้นทางผ่านพวกเขา
Steve Emmerson

11

ฉันบังคับให้ฉันใช้returnคำสั่งเพียงคำเดียวเพราะมันจะสร้างกลิ่นรหัสได้ ให้ฉันอธิบาย:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(เงื่อนไขเป็นสิทธิ์ ... )

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

  • ผลตอบแทนหลายรายการ
  • การปรับโครงสร้างเป็นฟังก์ชันแยกต่างหาก

ผลตอบแทนหลายรายการ

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

ฟังก์ชั่นแยก

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

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

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

5
-1: ตัวอย่างที่ไม่ดี คุณละเว้นการจัดการข้อความแสดงข้อผิดพลาด หากไม่จำเป็นต้องใช้ isCorrect สามารถแสดงผลได้เช่นเดียวกับ return xx && yy && zz; โดยที่ xx, yy และ z คือ isEqual, isDouble และ isThird expression
kauppi

10

ฉันจะบอกว่าคุณควรมีจำนวนมากตามที่ต้องการหรือสิ่งใดก็ตามที่ทำให้โค้ดสะอาด (เช่นคำสั่งป้องกัน )

ฉันไม่เคยได้ยิน / เห็น "แนวปฏิบัติที่ดีที่สุด" ใด ๆ เป็นการส่วนตัวว่าคุณควรมีข้อความสั่งการส่งคืนเดียว

ส่วนใหญ่ฉันมักจะออกจากฟังก์ชันโดยเร็วที่สุดตามเส้นทางตรรกะ (ส่วนคำสั่งป้องกันเป็นตัวอย่างที่ยอดเยี่ยมของเรื่องนี้)


10

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

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

มากกว่าหนึ่งสไตล์การส่งคืนอาจเป็นนิสัยที่ไม่ดีในรหัส C ซึ่งทรัพยากรจะต้องได้รับการจัดสรรอย่างชัดเจน แต่ภาษาเช่น Java, C #, Python หรือ JavaScript ที่มีโครงสร้างเช่นการรวบรวมขยะอัตโนมัติและtry..finallyบล็อก (และusingบล็อกใน C # ) และอาร์กิวเมนต์นี้ใช้ไม่ได้ - ในภาษาเหล่านี้เป็นเรื่องแปลกมากที่ต้องมีการจัดสรรทรัพยากรด้วยตนเองแบบรวมศูนย์

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

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

ผมได้พูดคุยเกี่ยวกับเรื่องนี้ที่มีความยาวมากขึ้นในบล็อกของฉัน


10

มีสิ่งที่ดีที่จะพูดเกี่ยวกับการมีจุดทางออกเดียวเช่นเดียวกับที่มีสิ่งเลวร้ายที่จะพูดเกี่ยวกับการเขียนโปรแกรม"ลูกศร" ที่หลีกเลี่ยงไม่ได้ที่เป็นผล

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

ทั้งบทความการเขียนโปรแกรมสปาร์ตันของ "SSDSLPedia" และฟังก์ชั่นบทความจุดทางออกเดียวของ "Wiki รูปแบบที่เก็บพอร์ตแลนด์" มีข้อโต้แย้งที่ลึกซึ้งเกี่ยวกับเรื่องนี้ นอกจากนี้แน่นอนยังมีโพสต์นี้ที่จะต้องพิจารณา

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

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

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

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

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

ขึ้นอยู่กับโครงการหรือแนวทางการเข้ารหัสรหัสส่วนใหญ่ของแผ่นบอยเลอร์สามารถถูกแทนที่ด้วยมาโคร ในฐานะที่เป็นโน้ตด้านข้างการทำลายมันด้วยวิธีนี้ทำให้ฟังก์ชั่น g0, g1, g2 นั้นง่ายมากในการทดสอบทีละอย่าง

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

ในระยะสั้น;

  • ผลตอบแทนน้อยกว่าผลตอบแทนมากมาย
  • ผลตอบแทนมากกว่าหนึ่งจะดีกว่าลูกศรขนาดใหญ่และส่วนคำสั่งทั่วไปก็โอเค
  • ข้อยกเว้นสามารถ / อาจแทนที่ 'ส่วนคำสั่งป้องกัน' ส่วนใหญ่เมื่อทำได้

ตัวอย่างที่เกิดปัญหาสำหรับ Y <0, เพราะมันพยายามที่จะเป็นอิสระชี้โมฆะ ;-)
ริช Kitzmueller

2
opengroup.org/onlinepubs/009695399/functions/free.html "ถ้า ptr เป็นตัวชี้โมฆะจะไม่มีการดำเนินการใด ๆ "
Henrik Gustafsson

1
ไม่มันจะไม่ผิดพลาดเพราะการส่งค่า NULL เป็นอิสระจะไม่มีการกำหนดไว้ มันเป็นความเข้าใจผิดที่น่ารำคาญที่คุณต้องทดสอบค่า NULL ก่อน
hlovdal

รูปแบบ "ลูกศร" ไม่ใช่ทางเลือกที่หลีกเลี่ยงไม่ได้ นี่คือการแบ่งขั้วที่ผิดพลาด
Adrian McCarthy

9

คุณจะรู้ว่าสุภาษิต - ความงามในสายตาของคนดู

บางคนสาบานNetBeansและบางส่วนจากความคิด IntelliJบางโดยงูหลามและบางส่วนโดยPHP

ในร้านค้าบางแห่งคุณอาจตกงานถ้าคุณยืนยันที่จะทำสิ่งนี้:

public void hello()
{
   if (....)
   {
      ....
   }
}

คำถามคือทั้งหมดที่เกี่ยวกับการมองเห็นและการบำรุงรักษา

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

เฮ้บัดดี้ (เหมือนอดีตลูกค้าเคยพูด) ทำสิ่งที่คุณต้องการตราบใดที่คุณรู้วิธีแก้ไขเมื่อฉันต้องการให้คุณแก้ไข

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

เรามักจะมีความผิดในการทำลายการทำข้อมูลให้เป็นมาตรฐาน, ขอบเขตMVPและMVC เราอินไลน์แทนที่จะสร้างฟังก์ชั่น เราใช้ทางลัด

ส่วนตัวผมเชื่อว่า PHP นั้นเป็นสิ่งที่ไม่ดี แต่ฉันรู้อะไร ข้อโต้แย้งเชิงทฤษฎีทั้งหมดเกิดขึ้นเพื่อพยายามทำตามกฎหนึ่งชุด

คุณภาพ = ความแม่นยำการบำรุงรักษาและผลกำไร

กฎอื่น ๆ ทั้งหมดหายไปเป็นพื้นหลัง และแน่นอนกฎนี้ไม่เคยจางหายไป:

ความเกียจคร้านเป็นคุณธรรมของโปรแกรมเมอร์ที่ดี


1
"เฮ้บัดดี้ (เหมือนอดีตลูกค้าเคยพูด) ทำในสิ่งที่คุณต้องการตราบเท่าที่คุณรู้วิธีแก้ไขเมื่อฉันต้องการให้คุณแก้ไข" ปัญหา: บ่อยครั้งที่คุณไม่ใช่คนที่ 'แก้ไข' มัน
Dan Barron

+1 สำหรับคำตอบนี้เพราะฉันเห็นด้วยกับที่ที่คุณกำลังไป แต่ไม่จำเป็นว่าคุณจะไปที่นั่นได้อย่างไร ฉันจะยืนยันว่ามีระดับความเข้าใจ นั่นคือความเข้าใจว่าพนักงาน A ได้รับประสบการณ์การเขียนโปรแกรมหลังจาก 5 ปีและ 5 ปีกับ บริษัท นั้นแตกต่างจากพนักงาน B อย่างมากบัณฑิตวิทยาลัยเพิ่งเริ่มต้นกับ บริษัท ประเด็นของฉันคือถ้าพนักงาน A เป็นคนเดียวที่สามารถเข้าใจรหัสได้มันจะไม่สามารถบำรุงรักษาได้และดังนั้นเราทุกคนควรพยายามเขียนรหัสที่พนักงาน B สามารถเข้าใจได้ นี่คือที่ซึ่งงานศิลปะที่แท้จริงอยู่ในซอฟต์แวร์
ฟรานซิสโรเจอร์ส

9

ฉันโน้มตัวไปข้างหน้าโดยใช้คำสั่งเพื่อกลับก่อนและออกจากที่ส่วนท้ายของวิธีการ กฎการเข้าและออกครั้งเดียวมีความสำคัญทางประวัติศาสตร์และเป็นประโยชน์อย่างยิ่งเมื่อต้องจัดการกับรหัสดั้งเดิมที่วิ่งไปถึง 10 หน้า A4 สำหรับวิธี C ++ เดียวที่มีการส่งคืนหลายครั้ง (และข้อบกพร่องมากมาย) เมื่อเร็ว ๆ นี้การปฏิบัติที่ดีที่ได้รับการยอมรับคือการทำให้วิธีการเล็ก ๆ ซึ่งทำให้หลายทางออกน้อยกว่าความต้านทานต่อความเข้าใจ ในตัวอย่าง Kronoz ต่อไปนี้คัดลอกมาจากด้านบนคำถามคือเกิดอะไรขึ้นใน// ส่วนที่เหลือของรหัส ... ?:

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

ฉันรู้ว่าตัวอย่างนั้นถูกประดิษฐ์ขึ้นมาบ้าง แต่ฉันจะถูกล่อลวงให้ปรับโครงสร้างลูปforeachลงในคำสั่ง LINQ ซึ่งอาจถือได้ว่าเป็นประโยคป้องกัน อีกครั้งในตัวอย่าง contrived เจตนาของรหัสไม่ชัดเจนและsomeFunction ()อาจมีผลข้างเคียงบางอย่างอื่น ๆ หรือผลที่อาจจะใช้ใน// ส่วนที่เหลือของรหัส ...

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

ให้ฟังก์ชัน refactored ต่อไปนี้:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

C ++ ไม่มีข้อยกเว้นหรือไม่ แล้วทำไมคุณถึงกลับมาnullแทนที่จะทิ้งข้อยกเว้นที่ระบุว่าการโต้เถียงนั้นไม่ได้รับการยอมรับ?
Maarten Bodewes

1
ตามที่ฉันระบุรหัสตัวอย่างจะถูกคัดลอกมาจากคำตอบก่อนหน้า ( stackoverflow.com/a/36729/132599 ) ตัวอย่างดั้งเดิมคืนค่าเป็นโมฆะและการสร้างใหม่เพื่อทิ้งข้อโต้แย้งไม่ใช่สาระสำคัญจนถึงจุดที่ฉันพยายามสร้างหรือตอบคำถามเดิม เป็นเรื่องของการปฏิบัติที่ดีแล้วใช่ฉันปกติจะ (ใน C #) โยน ArgumentNullException ในส่วนคำสั่งแทนที่จะส่งกลับค่า null
David Clarke

7

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

ต้องบอกว่าฉันเคยทำงานในห้องสมุดที่กำหนดมาตรฐานการเข้ารหัส 'หนึ่งคืนคำสั่งต่อฟังก์ชั่น' และฉันพบว่ามันค่อนข้างยาก ฉันเขียนโค้ดคำนวณตัวเลขจำนวนมากและมักจะมี 'กรณีพิเศษ' ดังนั้นโค้ดจึงค่อนข้างยากที่จะติดตาม ...


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

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

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

7

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

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


6

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


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

6

จุดทางออกเดียว - สิ่งอื่น ๆ ที่เท่าเทียมกัน - ทำให้โค้ดอ่านง่ายขึ้น แต่มีการจับ: การก่อสร้างที่เป็นที่นิยม

resulttype res;
if if if...
return res;

เป็นของปลอม "res =" นั้นไม่ดีไปกว่า "คืน" มันมีคำสั่งคืนเดียว แต่หลายจุดที่ฟังก์ชั่นจริงสิ้นสุด

หากคุณมีฟังก์ชั่นที่มีผลตอบแทนหลายรายการ (หรือ "res =" s) ก็มักจะเป็นความคิดที่ดีที่จะแบ่งออกเป็นฟังก์ชั่นเล็ก ๆ หลาย ๆ ที่มีจุดทางออกเดียว


6

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

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


5

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

Perl 6: ตัวอย่างที่ไม่ดี

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

คงจะเขียนได้ดีกว่านี้

Perl 6: ตัวอย่างที่ดี

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

หมายเหตุนี่เป็นเพียงตัวอย่างด่วน


โอเคทำไมเรื่องนี้ถึงถูกโหวต? มันไม่ชอบมันไม่ใช่ความเห็น
Brad Gilbert

5

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

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

คุณโหวตให้ผลตอบแทนเดี่ยว - ในรหัส C แต่ถ้าคุณเข้ารหัสด้วยภาษาที่มีการรวบรวมขยะและลอง .. ในที่สุดบล็อก?
แอนโทนี่

4

นี่อาจเป็นมุมมองที่ผิดปกติ แต่ฉันคิดว่าทุกคนที่เชื่อว่าจะต้องมีการส่งคืนงบหลายรายการโดยไม่ต้องใช้ดีบักเกอร์บนไมโครโปรเซสเซอร์ที่รองรับจุดพักฮาร์ดแวร์เพียง 4 แห่ง ;-)

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


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

ขึ้นอยู่กับดีบักเกอร์ของคุณด้วย
Maarten Bodewes

4

ยิ่งคุณส่งคืนงบมากขึ้นในฟังก์ชั่นความซับซ้อนที่สูงขึ้นในวิธีใดวิธีหนึ่ง หากคุณพบว่าตัวเองสงสัยว่าคุณมีข้อความสั่งคืนมากเกินไปคุณอาจต้องถามตัวเองว่ามีรหัสบรรทัดมากเกินไปในฟังก์ชันนั้นหรือไม่

แต่ไม่มีอะไรผิดปกติกับหนึ่ง / หลายข้อความกลับ ในบางภาษามันเป็นวิธีปฏิบัติที่ดีกว่า (C ++) มากกว่าในภาษาอื่น (C)

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