บล็อกอื่นเพิ่มความซับซ้อนของรหัสหรือไม่ [ปิด]


18

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

string CanLeaveWithoutUmbrella()
{
    if(sky.Color.Equals(Color.Blue))
    {
        return "Yes you can";
    }
    else
    {
        return "No you can't";
    }
}

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

(ฉันจำไม่ได้ว่าคนส่วนใหญ่พูดอะไรฉันอาจไม่ได้ถามอย่างอื่น)

string CanLeaveWithoutUmbrella()
{
    if(sky.Color.Equals(Color.Blue))
    {
        return "Yes you can";
    }
    return "No you can't";
}

คำถาม:มีความซับซ้อนเพิ่มขึ้นโดยไม่รวมelseบล็อกหรือไม่

ฉันอยู่ภายใต้การแสดงผลelseความตั้งใจระบุโดยตรงโดยระบุความจริงที่ว่ารหัสในบล็อกทั้งสองเกี่ยวข้องโดยตรง

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

นำตัวอย่างที่ง่ายของฉันไปใช้ (ไม่สนใจข้อเท็จจริงที่orผู้ดำเนินการเนื่องจากนี่เป็นตัวอย่างที่เข้าใจง่าย)

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color != Color.Blue)
    {
        return false;
    }
    return true;
}

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

หากมีelseบล็อกอยู่ใครก็ตามที่เพิ่มเงื่อนไขใหม่จะถูกบังคับให้ย้ายเนื้อหาของelseบล็อก (และถ้าพวกเขาขัดเงามันฮิวริสติกจะแสดงว่ารหัสนั้นไม่สามารถเข้าถึงได้ซึ่งจะไม่อยู่ในกรณีที่มีการifบังคับอีกข้อ) .

แน่นอนว่ายังมีวิธีอื่น ๆ ที่ควรกำหนดตัวอย่างที่เฉพาะเจาะจงอย่างไรก็ตามทั้งหมดนี้ป้องกันสถานการณ์ดังกล่าว แต่เป็นเพียงตัวอย่างเท่านั้น

ความยาวของตัวอย่างที่ฉันให้อาจบิดเบือนมุมมองภาพของสิ่งนี้ดังนั้นสมมติว่าพื้นที่ที่นำไปไว้ในวงเล็บนั้นไม่มีความสำคัญกับวิธีที่เหลือ

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


ในความเป็นจริงเมื่อมีคำสั่ง "if" ที่มีคำว่า "return" จะสามารถเห็นได้ว่า "guard block" ใน> 50% ของทุกกรณี ดังนั้นฉันคิดว่าความเข้าใจผิดของคุณอยู่ที่นี่ว่า "บล็อกป้องกัน" เป็นกรณีพิเศษและตัวอย่างของคุณเป็นกรณีปกติ
Doc Brown

2
@ AssTraTrailmix: ฉันยอมรับว่ากรณีดังกล่าวข้างต้นอาจอ่านได้มากขึ้นเมื่อเขียนelseประโยค (ตามรสนิยมของฉันมันจะยิ่งอ่านง่ายขึ้นเมื่อออกจากวงเล็บที่ไม่จำเป็น แต่ฉันคิดว่าตัวอย่างไม่ได้เลือกอย่างดีเพราะใน กรณีข้างต้นผมจะเขียนreturn sky.Color == Color.Blue(โดยหาก / อื่น) ตัวอย่างที่มีประเภทผลตอบแทนที่แตกต่างกันกว่าบูลอาจจะทำเรื่องนี้ให้ชัดเจน..
หมอสีน้ำตาล

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

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

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

คำตอบ:


27

ในมุมมองของฉันบล็อกอื่นชัดเจนจะดีกว่า เมื่อฉันเห็นสิ่งนี้:

if (sky.Color != Blue) {
   ...
}  else {
   ...
}

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

เมื่อฉันเห็นสิ่งนี้:

if (sky.Color != Blue) {
   ...
} 
return false;

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

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

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

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

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

ไม่นานหลังจากที่คำตอบดั้งเดิมนี้มีการค้นพบโค้ดเช่นนี้ในโครงการของฉัน:

Foobar merge(Foobar left, Foobar right) {
   if(!left.isEmpty()) {
      if(!right.isEmpty()) {
          Foobar merged = // construct merged Foobar
          // missing return merged statement
      }
      return left;
   }
   return right;
}

หากไม่ใส่การส่งคืนภายใน elses ความจริงที่ว่าคำสั่งการส่งคืนที่หายไปนั้นถูกมองข้ามไป หากใช้งานอื่นรหัสจะไม่ได้รวบรวม (แน่นอนสิ่งที่ฉันกังวลยิ่งกว่าคือการทดสอบที่เขียนในรหัสนี้ค่อนข้างแย่ที่จะไม่ตรวจจับปัญหานี้)


เกี่ยวกับการสรุปความคิดของฉันเกี่ยวกับมันฉันดีใจที่ฉันไม่ใช่คนเดียวที่คิดเช่นนี้ ฉันทำงานกับโปรแกรมเมอร์เครื่องมือและ IDE ที่ต้องการลบการelseบล็อก (อาจเป็นเรื่องที่น่ารำคาญเมื่อฉันถูกบังคับให้ทำเพื่อให้สอดคล้องกับอนุสัญญาสำหรับ codebase) ฉันก็สนใจที่จะเห็นจุดโต้กลับที่นี่
Selali Adobor

1
ฉันแตกต่างกันในเรื่องนี้ บางครั้งสิ่งที่ชัดเจนไม่ได้ดึงดูดคุณมากนักเนื่องจากไม่น่าจะทำให้เกิดข้อผิดพลาดทางตรรกะเล็กน้อยที่ OP กล่าวถึง - เป็นเพียงเสียงรบกวน แต่ส่วนใหญ่ฉันเห็นด้วยและบางครั้งฉันจะทำสิ่ง} // elseที่คนอื่นจะไปประนีประนอมระหว่างคนทั้งสอง
Telastyn

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

2
ฉันคิดว่าคุณมีความเข้าใจผิดเหมือนกับ OP - "ถ้า" กับ "return" ส่วนใหญ่จะเห็นเป็นบล็อกป้องกันดังนั้นคุณกำลังพูดถึงที่นี่เป็นกรณีพิเศษในขณะที่กรณีบล็อกบ่อยขึ้น IMHO สามารถอ่านได้โดยไม่ต้อง "อื่น".
Doc Brown

... ดังนั้นสิ่งที่คุณเขียนไม่ผิด แต่ IMHO ไม่คุ้มกับจำนวน upvotes ที่คุณได้รับ
Doc Brown

23

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

ifงบจำนวนมากเป็นหลักยาม พวกเขากำลังป้องกันการยกเลิกการอ้างถึงพอยน์เตอร์พอยน์เตอร์และอื่น ๆ "อย่าทำแบบนี้!" ข้อผิดพลาด และจะนำไปสู่การยกเลิกอย่างรวดเร็วของฟังก์ชั่นปัจจุบัน ตัวอย่างเช่น:

if (obj === NULL) {
    return NULL;
}
// further processing that now can assume obj attributes are set

ตอนนี้อาจดูเหมือนว่าelseประโยคจะสมเหตุสมผลมากที่นี่:

if (obj === NULL) {
    return NULL;
}
else if (obj.color != Blue) {
   // handle case that object is not blue
}

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

elements = tree.xpath(".//p");
if (elements === NULL) {
    return NULL;
}
p = elements[0]
if ((p.children === NULL) or (p.children.length == 0)) {
    return NULL;
}
for (c in p.children) {
    c.text = c.text.toUpperCase();
}
return p;

ผู้คุมไม่เพิ่มการเยื้องของรหัส ด้วยelseอนุประโยคงานจริงทั้งหมดจะเริ่มเลื่อนไปทางขวา:

elements = tree.xpath(".//p");
if (elements === NULL) {
    return NULL;
}
else {
    p = elements[0]
    if ((p.children === NULL) or (p.children.length == 0)) {
        return NULL;
    }
    else {
        for (c in p.children) {
            c.text = c.text.toUpperCase();
        }
        return p;
    }
}

สิ่งนี้เริ่มมีการเคลื่อนไหวที่ถูกต้องอย่างมีนัยสำคัญและนี่เป็นตัวอย่างที่น่าสนใจโดยมีเงื่อนไขการป้องกันเพียงสองประการ ผู้พิทักษ์เพิ่มเติมทุกคนจะเพิ่มระดับการเยื้อง โค้ดจริงฉันได้เขียน XML การประมวลผลหรือการใช้ฟีด JSON ที่มีรายละเอียดมากขึ้นสามารถซ้อนเงื่อนไข 3, 4 หรือมากกว่าได้อย่างง่ายดาย หากคุณใช้เสมอคุณจะelseได้เว้าแหว่ง 4, 5 หรือ 6 ระดับบางทีอาจจะมากกว่านั้น และนั่นทำให้เกิดความซับซ้อนของโค้ดและแน่นอนยิ่งใช้เวลามากขึ้นในการทำความเข้าใจว่าสิ่งใดสอดคล้องกับสิ่งใด รูปแบบที่รวดเร็วแบนและออกเร็วช่วยกำจัด "โครงสร้างส่วนเกิน" บางส่วนและดูเรียบง่ายขึ้น

ภาคผนวกความคิดเห็นเกี่ยวกับคำตอบนี้ทำให้ฉันรู้ว่าอาจไม่ชัดเจนว่าการจัดการ NULL / เปล่าไม่ได้เป็นเพียงเหตุผลเดียวสำหรับการรักษาความปลอดภัย ไม่เกือบ! ในขณะที่ตัวอย่างง่ายๆนี้มุ่งเน้นไปที่การจัดการ NULL / เปล่าและไม่มีเหตุผลอื่น ๆ การค้นหาโครงสร้างที่ลึกเช่น XML และ AST สำหรับเนื้อหา "ที่เกี่ยวข้อง" ตัวอย่างเช่นมักจะมีการทดสอบเฉพาะชุดยาวเพื่อคัดแยกโหนดที่ไม่เกี่ยวข้องและไม่สนใจ กรณี ชุดของ "ถ้าโหนดนี้ไม่เกี่ยวข้องผลตอบแทน" การทดสอบจะทำให้เกิดการดริฟท์แบบเดียวกับที่ NULL / เปล่ายามจะ และในทางปฏิบัติการทดสอบความเกี่ยวข้องที่ตามมามักจะถูกนำมารวมเข้ากับ NULL / เปล่ายามสำหรับโครงสร้างข้อมูลที่ลึกกว่านั้นคือการค้นหา - การตีสองครั้งสำหรับการเลื่อนไปทางขวา


2
นี่เป็นคำตอบที่มีรายละเอียดและถูกต้อง แต่ฉันจะเพิ่มในคำถาม มี "ข้อยกเว้น" ที่ฉันมักจะพบว่ามีelseบล็อกฟุ่มเฟือยเมื่อใช้ifบล็อกเพื่อใช้ข้อ จำกัด ที่จะต้องมีความพึงพอใจทางตรรกะสำหรับรหัสต่อไปนี้ทั้งหมดเช่นการตรวจสอบเป็นโมฆะ (หรือยามอื่น ๆ )
Selali Adobor

@AssortedTrailmix ฉันเชื่อว่าถ้าคุณแยกกรณีที่คุณสามารถออกก่อนกำหนดในthenประโยค (เช่นคำสั่งต่อเนื่องยาม) เป็นประจำแล้วไม่มีค่าพิเศษที่จะหลีกเลี่ยงelseข้อเฉพาะ แน่นอนมันจะลดความคมชัดและอาจเป็นอันตราย (โดยไม่สามารถระบุโครงสร้างทางเลือกที่ / ควรมี)
Jonathan Eunice

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

นี่เป็นคำอธิบายที่สมบูรณ์แบบของคดีที่หลีกเลี่ยงไม่ได้
Chris Schiffhauer

2
ฉันได้ห่อ API เพื่อไม่ให้ NULL ที่ไม่มีความหมายแทน
Winston Ewert

4

เมื่อฉันเห็นสิ่งนี้:

if(something){
    return lamp;
}
return table;

ฉันเห็นทั่วไปสำนวน รูปแบบทั่วไปซึ่งในหัวของฉันแปลเป็น:

if(something){
    return lamp;
}
else return table;

เพราะนี่เป็นสำนวนที่พบบ่อยมากในการเขียนโปรแกรมโปรแกรมเมอร์ที่เคยเห็นรหัสประเภทนี้มี 'แปล' โดยนัยในหัวของพวกเขาเมื่อใดก็ตามที่พวกเขาเห็นมันว่า 'แปลง' เป็นความหมายที่เหมาะสม

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

ตัวอย่างเช่นอ่านข้อความต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

คุณอ่านสิ่งนี้หรือไม่?

ฉันรักปารีสในฤดูใบไม้ผลิ

ถ้าเป็นเช่นนั้นคุณผิด อ่านข้อความอีกครั้ง. สิ่งที่เขียนจริงๆคือ

ฉันรักปารีสในฤดูใบไม้ผลิ

มีคำว่า "the" สองคำในข้อความ เหตุใดคุณจึงพลาดหนึ่งในสองข้อนี้

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

สิ่งเดียวกันกับสำนวนข้างต้น โปรแกรมเมอร์ที่ได้อ่านมากรหัสที่ใช้ในการที่เห็นนี้รูปแบบif(something) {return x;} return y;ของ พวกเขาไม่ต้องการ explciit elseเพื่อทำความเข้าใจความหมายของมันเพราะสมองของพวกเขา 'วางไว้ที่นั่นเพื่อพวกเขา' พวกเขารับรู้ว่ามันเป็นรูปแบบที่มีความหมายเป็นที่รู้จัก: "สิ่งที่อยู่หลังวงเล็บปิดจะทำงานเฉพาะเมื่อเป็นนัยelseของรุ่นก่อนหน้าif"

ดังนั้นในความคิดของฉันelseมันไม่จำเป็น แต่เพียงใช้สิ่งที่ดูเหมือนจะอ่านได้มากขึ้น


2

พวกเขาเพิ่มความยุ่งเหยิงที่มองเห็นในรหัสดังนั้นใช่พวกเขาอาจเพิ่มความซับซ้อน

อย่างไรก็ตามสิ่งที่ตรงกันข้ามก็เป็นความจริงการปรับเปลี่ยนที่มากเกินไปเพื่อลดความยาวของรหัสสามารถเพิ่มความซับซ้อนได้ (ไม่ใช่ในกรณีง่าย ๆ นี้แน่นอน)

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

ฉันไม่เห็นด้วยกับข้อความของคุณว่าจะช่วยให้คุณป้องกันข้อผิดพลาดเล็กน้อยในตรรกะ

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

bool CanLeaveWithoutUmbrella() {
    if(sky.Color == Color.Blue)
    {
        if (sky.Humidity == Humidity.High) 
        {
            return true;
        } 
    else if (sky.Humidity != Humidity.High)
    {
        return true;
    } else if (sky.Color == Color.Red) 
    {
            return true;
    }
    } else 
    {
       return false;
    }
}

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

ฉันอยากจะแนะนำต่อไปนี้:

falseฉันพบนี้ให้อ่านง่ายขึ้นเพราะผมสามารถดูได้อย่างรวดเร็วที่เริ่มต้นเป็น

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

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

  • หากกรณีเป็นเรื่องง่ายเหมือนที่นำเสนอแล้วคำตอบของฉันก็คือการละเว้นif elseข้อใด ๆเลย

    return (sky.Color == ColorBlue);

  • หากเป็นกรณีที่ซับซ้อนมากขึ้นด้วยส่วนคำสั่งต่าง ๆ ฉันจะตอบ:

    บูล CanLeaveWithoutUmbrella () {

    if(sky.Color == Color.Blue && sky.Humidity == Humidity.High) {
        return true;
    } else if (sky.Humidity != Humidity.High) {
        return true;
    } else if (sky.Color == Color.Red) {
        return true;
    }
    
    return false;
    

    }

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

    double CanLeaveWithoutUmbrella() {
    
        double risk = 0.0;
    
        if(sky.Color == Color.Blue && sky.Humidity == Humidity.High) {
            risk = 1.0;
        } else if (sky.Humidity != Humidity.High) {
            risk = 0.5;
        } else if (sky.Color == Color.Red) {
            risk = 0.8;
        }
    
        Log("value of risk:" + risk);
        return risk;
    

    }

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

ดังนั้นความเรียบง่ายของตัวอย่างจึงเป็นปัจจัยที่ควรพิจารณา


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

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

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

พักการแก้ไขคุณกำลังทำสิ่งที่ดีฉันกำลังพิมพ์ความคิดเห็นตอนนี้
Selali Adobor

1

แม้ว่ากรณี if-else ที่อธิบายมีความซับซ้อนมากขึ้นในสถานการณ์เชิงปฏิบัติส่วนใหญ่ (แต่ไม่ทั้งหมด) มันไม่สำคัญมาก

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

นี่คือเหตุผล

การปรากฏตัวของ 'อื่น' สร้างกรณีที่ 3 โดยนัยที่จะต้องมีการประเมิน - นั่นไม่ใช่ 'if' และ 'else' คุณอาจกำลังคิด (เหมือนตอนนั้น) WTF?

คำอธิบายเป็นเช่นนี้ ...

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

เปรียบเทียบสิ่งนี้กับกรณีที่มีเพียง 'if' และ no 'else' ในกรณีนั้นมีเพียงสองตัวเลือกคือเส้นทาง 'if' และ 'ไม่ใช่ถ้า' การประเมินสองกรณีมีความซับซ้อนน้อยกว่าสามกรณี

หวังว่านี่จะช่วยได้


ในไฟล์ออบเจ็กต์ทั้งสองกรณีจะไม่แสดงผลเป็น jmps เหมือนกันหรือไม่
Winston Ewert

@WinstonEwert - ใครจะคาดหวังว่าพวกเขาจะเหมือนกัน อย่างไรก็ตามสิ่งที่เรนเดอร์และสิ่งที่เราคาดว่าจะเรนเดอร์นั้นเป็นสองสิ่งที่แตกต่างกันไม่ว่าพวกมันจะทับซ้อนกันเท่าใด
ปาร์

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

1

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

def value(key):
    if key == FROB:
        return FOO
    elif key == NIK:
        return BAR
    [...]
    else:
        return BAZ

นี่ค่อนข้างชัดเจน - มันเทียบเท่ากับcaseคำสั่งหรือการค้นหาพจนานุกรมเวอร์ชันที่สูงกว่าของreturn foo == bar:

KEY_VALUES = {FROB: FOO, NIK: BAR, [...]}
DEFAULT_VALUE = BAZ

def value(key):
    return KEY_VALUES.get(key, default=DEFAULT_VALUE)

สิ่งนี้ชัดเจนยิ่งขึ้นแม้ว่าจำนวนโทเค็นจะสูงกว่า (32 vs 24 สำหรับรหัสต้นฉบับที่มีคู่คีย์ / ค่าสองคู่) แต่ความหมายของฟังก์ชั่นนั้นชัดเจนแล้วในตอนนี้:มันเป็นแค่การค้นหา

(สิ่งนี้มีผลที่ตามมาสำหรับการเปลี่ยนโครงสร้าง - หากคุณต้องการมีผลข้างเคียงถ้าkey == NIKคุณมีสามทางเลือก

  1. เปลี่ยนกลับเป็นif/ elif/ elsestyle และแทรกบรรทัดเดียวในkey == NIKเคส
  2. บันทึกผลลัพธ์การค้นหาเป็นค่าและเพิ่มifคำสั่งvalueสำหรับกรณีพิเศษก่อนส่งคืน
  3. วางifคำสั่งในการโทร

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

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


ฉันชอบตารางการค้นหาของคุณเขียนกรณีสวิทช์แบบง่าย และฉันรู้ว่ามันเป็นสำนวน Python ที่ได้รับการสนับสนุน อย่างไรก็ตามในทางปฏิบัติฉันมักพบว่าเงื่อนไขหลายทาง (aka caseหรือswitchข้อความ) มีลักษณะเหมือนกันน้อยกว่า ตัวเลือกหลายตัวเป็นเพียงการส่งคืนค่าตรง แต่จากนั้นหนึ่งหรือสองกรณีจำเป็นต้องดำเนินการเพิ่มเติม และเมื่อคุณค้นพบกลยุทธ์การค้นหาไม่เหมาะคุณต้องเขียนใหม่ในลักษณะที่แตกต่างกันโดยสิ้นเชิงif elif ... elseไวยากรณ์ที่
Jonathan Eunice

นั่นเป็นเหตุผลที่ฉันคิดว่าการค้นหาโดยปกติควรเป็นหนึ่งซับหรือฟังก์ชั่นเพื่อให้ตรรกะรอบ ๆ ผลการค้นหาแยกออกจากตรรกะของการค้นหาในครั้งแรก
l0b0

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

1

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

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

การใช้ตัวดำเนินการที่ประกอบไปด้วยตัวอย่างของคุณจะเป็นดังนี้:

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue)
               ? true
               : false;
}

เนื่องจากเป็นนิพจน์แบบบูลีนจึงควรทำให้ง่ายขึ้นเรื่อย ๆ

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue);
}

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


+1, OP คือ IMHO พูดคุยเกี่ยวกับกรณีทางพยาธิวิทยาซึ่งควรจะเขียนได้ดีกว่าวิธีที่คุณแสดงข้างต้น กรณีที่บ่อยขึ้นสำหรับ "ถ้า" กับ "ส่งคืน" คือประสบการณ์ของฉันในกรณี "ยาม"
Doc Brown

ฉันไม่รู้ว่าทำไมสิ่งนี้ถึงเกิดขึ้น แต่ผู้คนก็เพิกเฉยต่อย่อหน้าแรกของคำถาม ที่จริงแล้วคุณทุกคนใช้รหัสบรรทัดที่แน่นอนที่ฉันบอกว่าไม่ควรใช้ I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Selali Adobor

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

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

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

1

อีกสิ่งหนึ่งที่คิดในเรื่องนี้คือนิสัยที่แต่ละวิธีส่งเสริม

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

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

  • แน่นอนว่ามี 3 กรณีขึ้นไป (n) ซึ่งเรามักจะสนับสนุนให้ละทิ้ง if / else chain และใช้คำสั่ง case / switch หรือ polymorphism

หลักการชี้นำที่ฉันจำไว้ที่นี่คือวิธีการหรือฟังก์ชั่นควรมีวัตถุประสงค์เดียวเท่านั้น รหัสใด ๆ ที่มี if / else หรือคำสั่ง switch ใช้สำหรับการแตกสาขาเท่านั้น ดังนั้นควรสั้นอย่างไร้เหตุผล (4 บรรทัด) และมอบหมายเฉพาะวิธีที่ถูกต้องหรือให้ผลลัพธ์ที่ชัดเจน (เช่นตัวอย่างของคุณ) เมื่อรหัสของคุณสั้นมากมันก็ยากที่จะพลาดผลตอบแทนหลายรายการ :) เหมือนกับ "goto ถือว่าเป็นอันตราย" คำสั่งการแบรนช์ใด ๆ สามารถถูกใช้ในทางที่ผิดดังนั้นการใส่ความคิดที่สำคัญลงไปในคำสั่งอื่น ดังที่คุณกล่าวถึงการมีบล็อกอื่นให้สถานที่สำหรับรหัสเพื่อกอ คุณและทีมของคุณจะต้องตัดสินใจว่าคุณรู้สึกอย่างไร

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


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

@AssortedTrailmix ใช่แล้วคุณกำลังคิดเกี่ยวกับelseความหมายเครื่องมือการวิเคราะห์รหัสมักจะมองหาคำแนะนำที่ไม่เกี่ยวข้องเพราะพวกเขามักจะเน้นความเข้าใจผิดของการควบคุมการไหล elseหลังจากที่ifผลตอบแทนที่ได้รับการสูญเสียบิต if(1 == 1)มักทำให้เกิดการเตือนแบบเดียวกัน
Steve Jackson

1

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

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

ก่อนที่คุณจะตัดสินใจที่จะรวมหรือกำจัด ELSE ให้ตัดสินใจว่ามันหมายถึงอะไรซึ่งจะบอกคุณว่าควรจะมีหรือไม่


0

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

if(a == b) {
    if(c <= d) {
        if(e != f) {
            a.doSomeStuff();
            c.makeSomeThingsWith(b);
            f.undo(d, e);
            return 9;
        } else {
            e++;
            return 3;
        }
    } else {
        return 1;
    }
} else {
    return 0;
}

ด้วย:

if(a != b) {
    return 0;
}

if(c > d) {
    return 1;
}

if(e != f) {
    e++;
    return 3;
}

a.doSomeStuff();
c.makeSomeThingsWith(b);
f.undo(d, e);
return 9;

บล็อกที่สองของรหัสเปลี่ยนifคำสั่งที่ซ้อนกันเป็นส่วนคำสั่งป้องกัน สิ่งนี้จะลดการเยื้องและทำให้เงื่อนไขการส่งคืนบางส่วนชัดเจนขึ้น ("ถ้าหากa != bเราจะส่งคืน0!")


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

เราสามารถเขียนบล็อกแรกของโค้ดที่นี่เพื่อให้สามารถอ่านได้มากขึ้น:

if(a != b) {
    return 0;
} else if(c > d) {
    return 1;
} else if(e != f) {
    e++;
    return 3;
} else {
    a.doSomeStuff();
    c.makeSomeThingsWith(b);
    f.undo(d, e);
    return 9;
}

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

...
if(e != f) {
    e++;
    return 3;
}

g.doSomeLogicWith(a);

if(!g.isGood()) {
    return 4;
}

a.doSomeStuff();
...

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

แต่จริงๆแล้วมันสมเหตุสมผลแล้ว การใช้if/elseความหมายอย่างอ่อนหมายถึงความสัมพันธ์ระหว่างส่วนต่างๆ เราสามารถคาดการณ์ได้ว่าa != bเกี่ยวข้องกับอะไรc > dในif/elseรุ่นที่ถูกล่ามโซ่ การคาดคะเนนั้นจะทำให้เข้าใจผิดอย่างที่เราเห็นได้จากเวอร์ชั่นประโยคที่ว่าพวกเขาเป็นจริงไม่เกี่ยวข้องกัน นั่นหมายความว่าเราสามารถทำอะไรได้มากขึ้นด้วยส่วนคำสั่งที่เป็นอิสระเช่นจัดเรียงใหม่ (อาจเพิ่มประสิทธิภาพการสืบค้นหรือปรับปรุงการไหลของตรรกะ) เราสามารถเพิกเฉยต่อความรู้ความเข้าใจเมื่อเราผ่านพวกมันไป ในif/elseห่วงโซ่ฉันไม่แน่ใจว่าความสัมพันธ์แบบเท่ากันระหว่างaและbจะไม่ปรากฏขึ้นในภายหลัง

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

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

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

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color != Color.Blue)
    {
        return false;
    }
    return true;
}

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

ถ้าฉันพูดว่าถ้าสีของท้องฟ้าเป็นสีดำมันเป็นคืนที่ชัดเจนดังนั้นจึงไม่จำเป็นต้องมีร่ม (มีวิธีที่ง่ายกว่าในการเขียนสิ่งนี้ แต่มีวิธีที่ง่ายกว่าในการเขียนตัวอย่างทั้งหมด)

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color == Color.Blue)
    {
        return true;
    }

    if(sky.Color == Color.Black)
    {
        return true;
    }

    return false;
}

ในตัวอย่างที่ใหญ่กว่าข้างต้นฉันสามารถเพิ่มประโยคใหม่โดยไม่จำเป็นต้องคิดมาก ในการif/elseฉันไม่สามารถมั่นใจได้ว่าฉันจะไม่เลอะบางสิ่งบางอย่างโดยเฉพาะอย่างยิ่งถ้าตรรกะมีความซับซ้อนมากขึ้น

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


1
การแก้ไขคำถามของฉันครอบคลุมถึงกรณีนี้ เมื่อข้อ จำกัด ที่ต้องเป็นไปตามหลักเหตุผลสำหรับรหัสต่อไปนี้ทั้งหมดเช่นการตรวจสอบเป็นโมฆะ (หรือยามอื่น ๆ ) ฉันเห็นด้วยว่าการละเว้นการelseบล็อกเป็นสิ่งจำเป็นสำหรับการอ่านได้
Selali Adobor

1
เวอร์ชั่นแรกของคุณนั้นยากที่จะเข้าใจ แต่ฉันไม่คิดว่ามันจำเป็นโดยใช้ถ้า / ดูวิธีการที่ฉันเขียนมัน: gist.github.com/winstonewert/c9e0955bc22ce44930fb
Winston Ewert

@WinstonEwert ดูการแก้ไขเพื่อตอบสนองต่อความคิดเห็นของคุณ
cbojar

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

0

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

void DoOneOfTwoThings(bool which)
{
    if (which)
        DoThingOne();
    else
        DoThingTwo();
}

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

void DoOneOfTwoThings(bool which)
{
    if (which) {
        DoThingOne();
        return;
    }
    DoThingTwo();
}

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

void ProcessInputOfTypeOne(String input)
{
    if (InputIsReallyTypeTwo(input)) {
        ProcessInputOfTypeTwo(input);
        return;
    }
    ProcessInputDefinitelyOfTypeOne(input);

}

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

void ProcessInputOfTypeOne(String input)
{
    if (InputIsReallyTypeTwo(input))
        ProcessInputOfTypeTwo(input);
    else
        ProcessInputDefinitelyOfTypeOne(input);
}

... เพราะจงใจไม่ได้ตั้งค่าโครงสร้างขนานให้เบาะแสกับผู้อ่านในอนาคตว่าได้รับการป้อนข้อมูลที่เป็น "จริงๆพิมพ์สอง" ที่นี่เป็นที่ที่ผิดปกติ (ในชีวิตจริง,ProcessInputDefinitelyOfTypeOneจะไม่แยกฟังก์ชั่นดังนั้นความแตกต่างจะยิ่งสิ้นเชิง)


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

@ WinstonEert ความผิดปกติอาจเป็นคำที่ผิด แต่ฉันเห็นด้วยกับ Zack ว่าหากคุณมีค่าเริ่มต้น (กรณีที่ 1) และมีข้อยกเว้นหนึ่งข้อควรเขียนเป็น» if-> ทำสิ่งที่พิเศษและปล่อยให้«เป็นกลยุทธ์คืนต้น นึกถึงโค้ดซึ่งค่าเริ่มต้นรวมการตรวจสอบเพิ่มเติมมันจะเร็วกว่าในการจัดการกรณีพิเศษก่อน
โทมัสขยะ

when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards)นี้ดูเหมือนว่าจะตกอยู่ในกรณีที่ผมได้พูดคุยเกี่ยวกับ ตามหลักเหตุผลแล้วงานใด ๆProcessInputOfTypeOneนั้นขึ้นอยู่กับความจริงที่ว่า!InputIsReallyTypeTwo(input)ดังนั้นเราจึงจำเป็นต้องตรวจจับสิ่งนั้นนอกเหนือจากกระบวนการปกติของเรา โดยปกติProcessInputOfTypeTwo(input);จะเป็นthrow ICan'tProccessThatExceptionสิ่งที่จะทำให้ความคล้ายคลึงกันชัดเจนยิ่งขึ้น
Selali Adobor

@ WinstonEwert ฉันอาจจะใช้ถ้อยคำที่ดีกว่าใช่ ฉันคิดถึงสถานการณ์ที่เกิดขึ้นบ่อยๆ เมื่อเขียนโค้ดโทเค็นไนเซอร์ด้วยมือ: ใน CSS ตัวอย่างลำดับตัวอักษร " u+" มักจะแนะนำโทเค็น "ยูนิโคด - พิสัย" แต่ถ้าตัวอักษรถัดไปไม่ใช่เลขฐานสิบหก ดังนั้นคุณต้องสำรองข้อมูลและประมวลผล 'u' เป็นตัวระบุแทน ดังนั้นเครื่องสแกนหลักวนซ้ำไปที่ "สแกนโทเค็นยูนิโคด - พิสัย" ค่อนข้างไม่แน่ชัดและมันเริ่มต้นด้วย "... ถ้าเราอยู่ในกรณีพิเศษที่ผิดปกตินี้ให้สำรองข้อมูลแล้วเรียกรูทีนย่อย scan-an-identifier แทน"
zwol

@AssortedTrailmix แม้ว่าตัวอย่างที่ฉันคิดมานั้นไม่ได้มาจาก codebase แบบดั้งเดิมซึ่งอาจใช้ข้อยกเว้นไม่ได้นี่ไม่ใช่เงื่อนไขข้อผิดพลาดแต่เป็นรหัสที่การเรียกProcessInputOfTypeOneใช้การตรวจสอบที่ไม่แม่นยำ แต่เร็ว จับประเภทที่สองแทน
zwol

0

ใช่มีการเพิ่มขึ้นในความซับซ้อนเพราะข้ออื่นซ้ำซ้อน มันจะเป็นการดีกว่าถ้าคุณละทิ้งโค้ดที่ไม่ใช้งานและค่าเฉลี่ย มันอยู่ในคอร์ดเดียวกับการข้ามตัวระบุที่ซ้ำซ้อนthis(Java, C ++, C #) และละเว้นวงเล็บในreturnคำสั่งและอื่น ๆ

โปรแกรมเมอร์ที่ดีคิดว่า "ฉันจะเพิ่มอะไรได้บ้าง" ในขณะที่โปรแกรมเมอร์ที่ยอดเยี่ยมคิดว่า "ฉันจะลบอะไรได้บ้าง"


1
เมื่อฉันเห็นการละเว้นของelseบล็อกถ้ามันอธิบายในหนึ่งซับ (ซึ่งไม่บ่อย) มันมักจะมีเหตุผลประเภทนี้ดังนั้น +1 สำหรับการนำเสนออาร์กิวเมนต์ "เริ่มต้น" ที่ฉันเห็น แต่ฉันไม่ คิดว่าเป็นเหตุผลที่ดีพอ A good programmer things "what can I add?" while a great programmer things "what can I remove?".ดูเหมือนจะเป็นภาษิตทั่วไปที่ผู้คนจำนวนมากเหยียดเกินไปโดยไม่พิจารณาอย่างรอบคอบและโดยส่วนตัวฉันคิดว่านี่เป็นกรณีดังกล่าว
Selali Adobor

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

-1

ฉันสังเกตเห็นการเปลี่ยนใจในกรณีนี้

เมื่อถามคำถามเกี่ยวกับปีที่ผ่านมาฉันจะได้ตอบบางสิ่งเช่น:

»ควรจะมีเพียงคนเดียวentryและเป็นหนึ่งexitที่จะวิธีการ«และด้วยนี้ในใจผมจะได้แนะนำให้ refactor รหัสเพื่อ:

bool CanLeaveWithoutUmbrella()
{
    bool result

    if(sky.Color == Color.Blue)
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

จากมุมมองการสื่อสารมันเป็น ชัดเจนว่าควรทำอย่างไร Ifบางสิ่งบางอย่างเป็นกรณีมีอิทธิพลต่อผลในทางเดียว , จัดการกับมันในอีกทางหนึ่งelse

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

bool CanLeaveWithoutUmbrella()
{
    bool result=false

    if(sky.Color == Color.Blue)
    {
        result = true;
    }
    return result;
}

จากนั้นคำถามก็เกิดขึ้น: ทำไมต้องใช้ตัวแปรชั่วคราวเลย? ขั้นตอนต่อไปของการปรับโครงสร้างนำไปสู่:

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue)
    {
        return true;
    }
    return false;
}

ดังนั้นนี้เป็นที่ชัดเจนในสิ่งที่มันทำ ฉันจะก้าวไปอีกขั้นหนึ่ง:

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue) return true;
    return false;
}

หรือสำหรับ ใจเสาะ :

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue) { return true; }
    return false;
}

คุณสามารถอ่านได้เช่น: CanLeaveWithoutUmbrella คืนค่า a บูลล หากไม่มีอะไรพิเศษเกิดขึ้นมันจะคืนค่าเท็จ «นี่เป็นการอ่านครั้งแรกจากบนลงล่าง

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

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

ใช่ถูกต้องแล้ว แต่ข้อมูลที่ซ้ำซ้อน คุณมีกรณีเริ่มต้นและ "ยกเว้น" หนึ่งรายการซึ่งครอบคลุมโดยif -statement

เมื่อต้องรับมือกับโครงสร้างที่ซับซ้อนฉันจะเลือกกลยุทธ์อื่นโดยละเว้นifหรือเป็นพี่ชายswitchที่น่าเกลียดเลย


+1 ซ้ำซ้อนแสดงให้เห็นการเพิ่มขึ้นของความซับซ้อนไม่เพียง แต่เพิ่มความสับสน ฉันรู้ว่าฉันจบลงด้วยการต้องตรวจสอบรหัสสองครั้งที่เขียนอย่างนี้เพราะความซ้ำซ้อน
dietbuddha

1
คุณสามารถลบเคสหลังจากเคสเริ่มต้นด้วยSo this is clear in what it does...และขยายเคสได้หรือไม่? (กรณีก่อนหน้านี้มีความเกี่ยวข้องที่น่าสงสัยเช่นกัน) ฉันพูดแบบนี้เพราะมันเป็นคำถามที่ขอให้เราตรวจสอบ คุณได้ระบุจุดยืนของคุณละเว้นบล็อกอื่นและจริง ๆ แล้วท่าทางที่ต้องการคำอธิบายเพิ่มเติม (และอีกอันที่ให้ฉันถามคำถามนี้) จากคำถาม:I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Selali Adobor

@AssortedTrailmix แน่นอน! สิ่งที่ฉันควรทำอย่างละเอียด? เนื่องจากคำถามเริ่มต้นของคุณ»บล็อกอื่นเพิ่มความซับซ้อนของรหัสหรือไม่«และคำตอบของฉันคือ: ใช่ ในกรณีนี้อาจถูกตัดออก
โทมัสขยะ

และไม่! ฉันไม่เข้าใจ downvote
โทมัสขยะ

-1

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

return true;

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

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

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

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

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

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

มีการเพิ่มความซับซ้อนในการแนะนำโดยไม่รวมบล็อกอื่นหรือไม่?

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


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

@MichaelShaw ฉันคิดว่าคำตอบของ Aviv Cohn เป็นสิ่งที่ฉันกำลังพูดถึง
Panzercrisis

-2

ในตัวอย่างที่คุณให้ไว้

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

ฉันคิดว่ามันไม่ง่ายไปกว่านี้:

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue);
}

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

-4

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

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        if(this.color == Color.Blue)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

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

ประการแรกมีคำถามของคุณเกี่ยวกับelseสาขา ฉันไม่คิดอย่างใด elseแต่มีแนวโน้มที่จะปล่อยออกมา ผมเข้าใจข้อโต้แย้งเกี่ยวกับการเป็นที่ชัดเจน แต่ความเห็นของผมก็คือว่าifงบควรห่อ 'ตัวเลือก' สาขาเช่นreturn trueหนึ่ง ฉันจะไม่เรียกreturn falseสาขา 'ไม่จำเป็น' เนื่องจากมันทำหน้าที่เป็นค่าเริ่มต้นของเรา ทั้งสองวิธีฉันเห็นว่านี่เป็นปัญหาเล็กน้อยมากและมีความสำคัญน้อยกว่าประเด็นต่อไปนี้:

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        if(this.color == Color.Blue)
        {
            return true;
        }
        return false;
    }
}

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

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        return (this.color == Color.Blue)
            ? true
            : false;
    }
}

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

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        return (this.color == Color.Blue);
    }
}

สิ่งต่อไปที่ผมจะชี้ให้เห็นว่า API ของคุณมีสองลบ: เป็นไปได้เพื่อที่จะเป็นCanLeaveWithoutUmbrella falseนี้แน่นอนช่วยลดความยุ่งยากในการNeedUmbrellaเป็นอยู่trueจึงขอทำที่:

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool NeedUmbrella()
    {
        return (this.color != Color.Blue);
    }
}

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

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

class Sky {
    bool NeedUmbrella()
    {
        return true;
    }
}

class BlueSky extends Sky {
    bool NeedUmbrella()
    {
        return false;
    }
}

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


ย่อหน้าแรกของคำตอบดูเหมือนจะติดตามการตอบคำถาม แต่ทันทีที่แยกออกเป็นหัวข้อที่แน่นอนฉันถามอย่างชัดเจนละเว้นหนึ่งสำหรับตัวอย่างง่าย ๆนี้ จากคำถาม: I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.). อันที่จริงคุณใช้รหัสบรรทัดที่ฉันพูดถึง นอกจากนี้ในขณะที่คำถามนี้อยู่นอกหัวข้อฉันจะบอกว่าคุณไปไกลเกินกว่าที่จะอนุมานได้ คุณได้เปลี่ยนแปลงอย่างรุนแรงว่ารหัสคืออะไร
Selali Adobor

@AssortedTrailmix นี่เป็นคำถามของสไตล์ล้วนๆ มันสมเหตุสมผลที่จะใส่ใจสไตล์และมันสมเหตุสมผลที่จะไม่สนใจมัน (เน้นเฉพาะผลลัพธ์เท่านั้น) ฉันไม่คิดว่ามันทำให้รู้สึกถึงการดูแลเกี่ยวกับการreturn false;เทียบกับelse { return false; }ในขณะที่ไม่ได้ดูแลเกี่ยวกับขั้วสาขาจัดส่งแบบไดนามิก ternaries ฯลฯ หากคุณกำลังเขียนรหัสขั้นตอนในภาษา OO, การเปลี่ยนแปลงที่รุนแรงเป็นสิ่งที่จำเป็น;)
Warbo

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

ฉันไม่เคยบอกว่าโซลูชัน OO นั้นดีกว่า (อันที่จริงฉันชอบการเขียนโปรแกรมแบบใช้งานได้) ฉันแค่บอกว่ามันสอดคล้องกับภาษามากขึ้น โดย "ขั้ว" ฉันหมายถึงสิ่งที่เป็น "จริง" และ "เท็จ" (ฉันเปลี่ยนเป็น "NeedUmbrella") ในการออกแบบ OO การควบคุมการไหลทั้งหมดจะถูกกำหนดโดยการกระจายแบบไดนามิก ตัวอย่างเช่น Smalltalk ไม่อนุญาตสิ่งอื่นen.wikipedia.org/wiki/Smalltalk#Control_structures (โปรดดูc2.com/cgi/wiki?HeInventedTheTerm )
Warbo

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