ความสามารถในการบำรุงรักษาของบูลีนลอจิก - ทำรังหากต้องการข้อความ?


11

ข้อไหนดีกว่าในการบำรุงรักษา

if (byteArrayVariable != null)
    if (byteArrayVariable .Length != 0)
         //Do something with byteArrayVariable 

หรือ

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

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

นี่เป็นเพราะคุณพึ่งพาภาษาที่จะไม่ประเมินส่วนที่สองของifถ้าส่วนแรกเป็นเท็จและไม่ใช่ทุกภาษาที่ทำเช่นนั้น (ส่วนที่สองจะโยนข้อยกเว้นถ้าประเมินด้วย null byteArrayVariable)

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

ขอบคุณ


1
แบบฟอร์มแรกมีความเสี่ยงต่อการห้อย - ของอื่น
JBRWilkinson

แค่อยากรู้อยากเห็นคุณใช้ภาษาอะไร
STW

@STW - เราใช้ C # และมีรหัสดั้งเดิมใน Delphi
Vaccano

2
ดีแล้วที่รู้. ฉันไม่คุ้นเคยกับ Delphi แต่ใน C # คุณสามารถมั่นใจได้ใน&&และ||ผู้ประกอบการดำเนินการประเมินผลการลัดวงจร การเปลี่ยนจากภาษาหนึ่งไปเป็นอีกภาษาหนึ่งมักจะมี gotchas อยู่บ้างและเป็นการดีที่คุณควรทำการตรวจสอบโค้ดเพื่อช่วยแก้ไขปัญหาเล็กน้อยเหล่านั้น
STW

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

คำตอบ:


30

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

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

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


12
ตกลง ทำไมบนโลกนี้ฉันจะสนใจถ้ารหัสของฉันจะทำงานเหมือนกันเมื่อคัดลอกเป็นภาษาอื่น?
ทิมกู๊ดแมน

16

ฉันประหลาดใจที่ไม่มีใครพูดถึงมัน (ดังนั้นหวังว่าฉันจะไม่ได้อ่านคำถามผิด ๆ ) - แต่ทั้งคู่ก็ดูด!

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

ณ จุดนี้คุณจะทำสิ่งที่ชอบ:

if ((byteArrayVariable == null) || (byteArrayVariable.Length != 0)) {
  return; // nothing to be done, leaving
}

// Conditions met, do something

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

หากรหัสของคุณเป็นสปาเก็ตตี้จำนวนมาก (วิธีการ loooong, ifs ที่ซ้อนกันจำนวนมากที่มีตรรกะกระจัดกระจายระหว่างพวกเขา ฯลฯ ) ดังนั้นคุณควรมุ่งเน้นไปที่ปัญหาที่ใหญ่กว่าในการทำให้โค้ดของคุณมีรูปร่างที่เล็กลง ขึ้นอยู่กับสภาพแวดล้อมของคุณและความรุนแรงของความยุ่งเหยิงมีเครื่องมือที่ยอดเยี่ยมที่จะช่วย (เช่น Visual Studio มีฟังก์ชั่น "แยกวิธี" การปรับโครงสร้าง)


ดังที่จิมกล่าวถึงยังคงมีห้องสำหรับอภิปรายเกี่ยวกับวิธีจัดโครงสร้างทางออกก่อน ทางเลือกสำหรับสิ่งที่อยู่ด้านบนจะเป็น:

if (byteArrayVariable == null) 
  return; // nothing to be done, leaving

if (byteArrayVariable.Length != 0)
  return;

// Conditions met, do something

1
ฉันเห็นด้วยกับการส่งคืนแทนที่จะใช้ตรรกะที่ดี แต่คนคนเดียวกันจะใช้อาร์กิวเมนต์เดียวกันเกี่ยวกับการใช้ || ส่วน &&.
MIA

14

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

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
// do something

วิธีนี้ช่วยให้คุณปรับรหัสของคุณได้ง่ายขึ้นสำหรับเงื่อนไขใหม่

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
if (NewCondition == false) Exit;
if (NewerCondition == true) Exit;
// do something

2
+1 Yup, yup, yup ตรรกะอย่างง่ายอย่างนี้ไม่ควรทำให้โค้ดยาก ลำไส้ของคุณ (ผู้ถาม) ควรจะบอกคุณเรื่องนี้
งาน

11

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

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

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

ฉันมักจะชอบการสื่อสารที่ชัดเจนของความตั้งใจของฉันมากกว่าการยืนยันคนโง่ที่ว่า "ไม่ทำงานในทุกภาษา" คาดเดาสิ่งที่ ... throwไม่ทำงานในทุกภาษาเช่นกันหมายความว่าฉันไม่ควรใช้มันเมื่อการเข้ารหัสใน C # หรือ Java และควรพึ่งพาค่าส่งคืนเพื่อส่งสัญญาณเมื่อมีปัญหาหรือไม่

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


มากกว่าสองเงื่อนไขอาจรับประกันการทำงานของตัวเองซึ่งคืนค่าเป็นศูนย์
งาน

3

ในกรณีนี้ข้อสองข้อของคุณเกี่ยวข้องโดยตรงดังนั้นจึงควรเลือกแบบฟอร์มที่ 2

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


1

หากคุณทำงานใน Delphi วิธีแรกคือโดยทั่วไปปลอดภัยกว่า (สำหรับตัวอย่างที่ให้มามีไม่มากที่จะได้รับจากการใช้ ifs ที่ซ้อนกัน)

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


1
ใน 23 ปีของการพัฒนา Delphi ฉันไม่เคยเปลี่ยนตัวเลือก "Complete boolean eval" หากฉันเป็นรหัสส่งของที่อื่นฉันจะวาง {$ BOOLEVAL OFF} หรือ {$ B-} ไว้ในหน่วย
เจอร์รี่

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

@Gerry: เห็นด้วย เหตุผลเดียวที่คุณอาจต้องการประเมินแบบเต็มคือผลข้างเคียงและผลข้างเคียงในเงื่อนไขเป็นความคิดที่ไม่ดี!
Loren Pechtel

แค่อ่านความคิดเห็นของฉันอีก 23 ปีควรเป็น 13! ฉันไม่มีควานหา
Gerry

1

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


2
นั่นเป็นเพราะ VBA เป็นภาษาที่แย่มาก
Falmarri

@Falmarri มีบางสิ่งที่น่ากลัวเกี่ยวกับมัน (และบางสิ่งที่ดี) ฉันจะแก้ไขสิ่งต่าง ๆ มากมายถ้ามี druthers ของฉัน

2
การขาดการประเมินผลแบบบูลีนลัดวงจรเป็นสิ่งที่สืบทอดมากกว่าสิ่งใด ไม่รู้เลยว่าทำไมพวกเขาถึงไม่เริ่มด้วย VB.NET "แก้ไข" ของ OrElse และ AndAlso ค่อนข้างน่ารำคาญ แต่อาจเป็นทางเลือกเดียวที่จะจัดการกับมันโดยไม่ทำลายความเข้ากันได้แบบย้อนหลัง
JohnFx

1

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


0

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

ในบางภาษาตัวเลือกที่สองจะทำงานได้ดีกว่าดังนั้นจึงเป็นตัวเลือกที่ชัดเจน


0

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

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


0

ฉันชอบตัวเลือกที่สองเพราะอ่านได้มากกว่า

หากตรรกะที่คุณใช้มีความซับซ้อนมากขึ้น (การตรวจสอบว่าสตริงไม่ใช่โมฆะและไม่ว่างเปล่าเป็นเพียงตัวอย่าง) จากนั้นคุณสามารถสร้างการปรับโครงสร้างที่มีประโยชน์: แยกฟังก์ชันบูลีน ฉันใช้ @mipadi ในหมายเหตุ "Code Complete" ("ไม่สำคัญว่าทุกภาษาจะทำเช่นนั้น ... ") หากผู้อ่านรหัสของคุณไม่สนใจที่จะดูในฟังก์ชั่นที่แยกออกมา ซึ่งตัวถูกดำเนินการบูลีนถูกประเมินกลายเป็นคำถามที่สงสัย


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