One-liners vs. readability: เมื่อใดที่จะหยุดลดรหัส? [ปิด]


14

บริบท

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

ทุกวันนี้ฉันส่วนใหญ่เป็นรหัสใน Ruby ดังนั้นฉันจึงเริ่มใช้ linter (Rubocop) เพื่อให้ข้อมูลบางอย่างเกี่ยวกับ "คุณภาพ" ของรหัสของฉัน ("คุณภาพ" นี้ถูกกำหนดโดยชุมชนruby-style- project -guide-guide )

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

อย่างไรก็ตามทำทุกสิ่งที่ฉันรู้ (หรืออย่างน้อยก็จำได้) บางสิ่ง:

  • บางภาษา (ที่โดดเด่นที่สุดคือ Python, Ruby และเช่นนั้น) อนุญาตให้สร้างโค้ดแบบหนึ่งบรรทัดที่ยอดเยี่ยม
  • การปฏิบัติตามหลักเกณฑ์บางประการสำหรับรหัสของคุณอาจทำให้รหัสสั้นลงและชัดเจนมาก
  • กระนั้นก็ตามการปฏิบัติตามแนวทางเหล่านี้อย่างเคร่งครัดเกินไปอาจทำให้รหัสชัดเจนน้อยลง / อ่านง่าย
  • รหัสสามารถปฏิบัติตามหลักเกณฑ์บางอย่างเกือบจะสมบูรณ์แบบและยังคงมีคุณภาพไม่ดี
  • ส่วนใหญ่การอ่านรหัสเป็นเรื่องส่วนตัว (เช่นเดียวกับใน "สิ่งที่ฉันเห็นชัดเจนว่าอาจคลุมเครือกับนักพัฒนาซอฟต์แวร์")

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

ตอนนี้ตัวอย่างบางส่วนเพื่อทำให้ทุกอย่างชัดเจนยิ่งขึ้น

ตัวอย่าง

มาดูกันดีกว่า: เรามีแอพพลิเคชั่นที่มีUserรูปแบบ "" ผู้ใช้มีตัวเลือกfirstnameและsurnameและที่emailอยู่บังคับ

ผมอยากจะเขียนวิธีการ " name" ซึ่งจะกลับมาแล้วชื่อ ( firstname + surname) ของผู้ใช้ถ้าอย่างน้อยเขาfirstnameหรือsurnameเป็นปัจจุบันหรือมันemailเป็นค่าทางเลือกหากไม่ได้

ฉันต้องการให้วิธีนี้ใช้use_emailพารามิเตอร์" " เป็นบูลีน (อนุญาตให้ใช้อีเมลผู้ใช้เป็นค่าทางเลือก) use_emailพารามิเตอร์ " " นี้ควรเป็นค่าเริ่มต้น (หากไม่ผ่าน) เป็น " true"

วิธีที่ง่ายที่สุดในการเขียนใน Ruby คือ:

def name(use_email = true)
 # If firstname and surname are both blank (empty string or undefined)
 # and we can use the email...
 if (firstname.blank? && surname.blank?) && use_email
  # ... then, return the email
  return email
 else
  # ... else, concatenate the firstname and surname...
  name = "#{firstname} #{surname}"
  # ... and return the result striped from leading and trailing spaces
  return name.strip
 end
end

รหัสนี้เป็นวิธีที่ง่ายและเข้าใจง่ายที่สุด แม้แต่คนที่ไม่ "พูด" รูบี

ทีนี้ลองทำให้สั้นลง:

def name(use_email = true)
 # 'if' condition is used as a guard clause instead of a conditional block
 return email if (firstname.blank? && surname.blank?) && use_email
 # Use of 'return' makes 'else' useless anyway
 name = "#{firstname} #{surname}"
 return name.strip
end

สั้นกว่านี้ยังเข้าใจง่ายถ้าไม่ง่ายกว่า (ส่วนคำสั่งป้องกันอ่านง่ายกว่าบล็อกแบบมีเงื่อนไข) ประโยค Guard ยังทำให้สอดคล้องกับแนวทางที่ฉันใช้มากกว่าดังนั้น win-win ที่นี่ เรายังลดระดับการเยื้อง

ทีนี้ลองใช้เวทมนต์ Ruby เพื่อทำให้มันสั้นลง:

def name(use_email = true)
 return email if (firstname.blank? && surname.blank?) && use_email
 # Ruby can return the last called value, making 'return' useless
 # and we can apply strip directly to our string, no need to store it
 "#{firstname} #{surname}".strip
end

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

ที่นี่เราสามารถเริ่มถามคำถาม: คุ้มหรือไม่ เราควรพูดว่า "ไม่ทำให้อ่านได้และเพิ่มreturn" "" (การรู้ว่าสิ่งนี้จะไม่เคารพหลักเกณฑ์) หรือเราควรจะพูดว่า "ไม่เป็นไรเป็นวิธีเรียนรู้ภาษาที่น่ากลัว"

หากเราใช้ตัวเลือก B ดังนั้นทำไมไม่ทำให้สั้นลง:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end

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

นอกจากนี้เรายังสามารถเขียนมัน:

def name(use_email = true)
 (email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end

มันสั้นไม่ยากที่จะอ่าน (ฉันหมายถึงเราทุกคนได้เห็นสิ่งที่น่าเกลียดหนึ่งซับสามารถมีลักษณะ), ดีทับทิมมันเป็นไปตามแนวทางที่ฉันใช้ ... แต่ยังเทียบกับวิธีแรกในการเขียน มันอ่านและเข้าใจได้ง่ายกว่ามาก เราสามารถยืนยันได้ว่าบรรทัดนี้ยาวเกินไป (มากกว่า 80 ตัวอักษร)

คำถาม

ตัวอย่างของรหัสสามารถแสดงให้เห็นว่าการเลือกระหว่างรหัส "ขนาดเต็ม" และหลาย ๆ รุ่นที่ลดลง (จนถึงหนึ่งซับที่มีชื่อเสียง) อาจเป็นเรื่องยากเนื่องจากอย่างที่เราเห็นว่าหนึ่งซับไม่น่ากลัว แต่ ยังไม่มีอะไรจะเอาชนะรหัส "ขนาดเต็ม" ในแง่ของการอ่าน ...

ดังนั้นนี่คือคำถามจริง: หยุดที่ไหน สั้นเมื่อใดจะสั้นพอ จะทราบได้อย่างไรว่าเมื่อรหัสกลายเป็น "สั้นเกินไป" และอ่านได้น้อยลง (โปรดจำไว้ว่ามันเป็นอัตวิสัย) และยิ่งไปกว่า: วิธีการเขียนโค้ดให้สอดคล้องกันเสมอและหลีกเลี่ยงการผสม one-liners กับ chunks "full-size" เมื่อฉันรู้สึกเหมือนมัน?

TL; DR

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

คำถามหลักไม่ใช่คำถามคลาสสิก "One-liners vs. readability: อันไหนดีกว่ากัน?" แต่ "จะหาสมดุลระหว่างทั้งสองได้อย่างไร"

แก้ไข 1

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


7
สั้นเกินไปสำหรับคำตอบ: ให้ทำการรีเฟรชซ้ำ ๆ ซ้ำ ๆ จนกว่าคุณจะไม่แน่ใจว่ามันดีกว่าการทำซ้ำก่อนหน้านี้จากนั้นหยุดและย้อนกลับการรีแฟคเตอร์ซ้ำครั้งสุดท้าย
Dom

8
ฉันไม่ต้องการที่แตกต่างกัน 3 กับreturnคำหลักเพิ่ม ตัวละครทั้งเจ็ดนั้นเพิ่มความชัดเจนในสายตาของฉัน
cmaster - คืนสถานะโมนิก้า

2
หากคุณรู้สึกน่ากลัวจริง ๆ คุณสามารถเขียนสิ่งทั้งหมดเป็น[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip... เพราะfalse.blank?ผลตอบแทนจริงและผู้ประกอบการที่ช่วยคุณประหยัดสองสามตัวละคร ... ¯ \ _ (ツ) _ / ¯
DaveMongoose

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

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

คำตอบ:


26

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

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

ก่อนอื่นคุณลักษณะและวิธีการเขียน Ruby คือการละเว้นreturnคำหลักเมื่อส่งคืนค่ายกเว้นคืนจากวิธีแรก

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

อ่านออกเสียงดังต่อไปนี้เพื่อตัวคุณเอง

5.times { print "Odelay!" }

ในประโยคภาษาอังกฤษเครื่องหมายวรรคตอน (เช่นจุด, เครื่องหมายอัศเจรีย์, วงเล็บ) จะเงียบ เครื่องหมายวรรคตอนเพิ่มความหมายให้กับคำช่วยให้ความหมายตามสิ่งที่ผู้แต่งตั้งใจโดยประโยค ดังนั้นเรามาอ่านข้างต้นเป็น: ห้าครั้งพิมพ์ "Odelay!"

รับนี้ตัวอย่างรหัส # 3 เป็นสำนวนที่สุดสำหรับทับทิม:

def name(use_email = true)
  return email if firstname.blank? && surname.blank? && use_email

  "#{firstname} #{surname}".strip
end

ตอนนี้เมื่อเราอ่านโค้ดมันบอกว่า:

ส่งคืนอีเมลถ้าชื่อว่างเปล่าและนามสกุลว่างเปล่าและใช้อีเมล

(คืน) ชื่อแรกและนามสกุลถูกปล้น

ซึ่งค่อนข้างใกล้เคียงกับรหัสทับทิมจริง

มันเป็นโค้ดจริงเพียง 2 บรรทัดดังนั้นมันจึงค่อนข้างสั้นและเป็นไปตามสำนวนภาษา


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

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

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

โครงสร้างรหัสตามโครงสร้างภาษาธรรมชาติสามารถทำให้คุณตกหลุมพรางภาษา ตัวอย่างเช่นเมื่อคุณอ่านข้อกำหนดต่อไปdo send an email if A, B, C but no Dตามสถานที่ตั้งของคุณจะเป็นธรรมชาติที่จะพิมพ์ลง 2 หาก / อื่นif not D, send an emailบล็อกเมื่ออาจจะง่ายต่อการรหัส โปรดใช้ความระมัดระวังในช่วงเวลาของการอ่านภาษาธรรมชาติและแปลงมันเป็นรหัสเพราะมันสามารถทำให้คุณเขียนรุ่นใหม่ของ"เรื่องไม่มีที่สิ้นสุด" ด้วยชั้นเรียนวิธีการและตัวแปร ไม่ใช่เรื่องใหญ่หลังจากทั้งหมด
Laiv

@Laiv: การทำให้โค้ดอ่านเหมือนภาษาธรรมชาติไม่ได้แปลความหมายตามตัวอักษรอย่างแท้จริง มันหมายถึงการเขียนโค้ดเพื่อที่ว่าเมื่ออ่านออกมาดัง ๆ มันจะช่วยให้ผู้อ่านเข้าใจตรรกะโดยไม่ต้องอ่านโค้ดทุกบิตตัวอักษรสำหรับตัวละครภาษาที่สร้างขึ้นสำหรับการสร้างภาษา ถ้าการเข้ารหัสif !Dดีกว่านั่นก็ไม่เป็นเรื่องที่Dมีความหมาย และหาก!ผู้ประกอบการหลงทางในรหัสอื่นการมีตัวระบุที่เรียกว่าNotDเหมาะสม
Greg Burghardt

15

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

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


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

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

2
@Sudiukil: นั่นคือประเด็นสำคัญ ฉันขอแนะนำให้คุณพยายามใช้รหัสสำนวนในกรณีนั้น (เช่นสมมติว่ามีความรู้ด้านภาษาที่ดี) เนื่องจากผู้เริ่มต้นไม่น่าจะมีส่วนร่วมในการเปิดซอร์สโค้ดอยู่ดี (หรือถ้าพวกเขาทำพวกเขาจะพร้อมที่จะพยายามเรียนภาษา)
JacquesB

7

ส่วนหนึ่งของปัญหาที่นี่คือ "สิ่งที่อ่านได้" สำหรับฉันฉันดูตัวอย่างรหัสแรกของคุณ:

def name(use_email = true)
 # If firstname and surname are both blank (empty string or undefined)
 # and we can use the email...
 if (firstname.blank? && surname.blank?) && use_email
  # ... then, return the email
  return email
 else
  # ... else, concatenate the firstname and surname...
  name = "#{firstname} #{surname}"
  # ... and return the result striped from leading and trailing spaces
  return name.strip
 end
end

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

def name(use_email = true)
 if (firstname.blank? && surname.blank?) && use_email
  return email
 else
  name = "#{firstname} #{surname}"
  return name.strip
 end
end

และตอนนี้สามารถอ่านได้มากขึ้น เมื่ออ่านแล้วฉันคิดว่า "อืมฉันสงสัยว่า Ruby รองรับผู้ประกอบการที่ประกอบไปด้วยหรือไม่ใน C # ฉันสามารถเขียนเป็น:

string Name(bool useEmail = true) => 
    firstName.Blank() && surname.Blank() && useEmail 
    ? email 
    : $"{firstname} {surname}".Strip();

เป็นไปได้ไหมที่ทับทิม? ทำงานผ่านโพสต์ของคุณฉันเห็นมี:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end

ทุกสิ่งที่ดี แต่นั่นไม่สามารถอ่านได้สำหรับฉัน เพียงเพราะฉันต้องเลื่อนเพื่อดูทั้งบรรทัด ดังนั้นขอแก้ไขนั่น:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) 
 || "#{firstname} #{surname}".strip
end

ตอนนี้ฉันมีความสุข ฉันไม่แน่ใจว่าไวยากรณ์ทำงานอย่างไร แต่ฉันเข้าใจได้ว่าโค้ดทำอะไร

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

สิ่งหนึ่งที่ฉันจะพูดว่า: ระวัง "รหัสที่ฉลาด" เช่นในตัวอย่างสุดท้ายของคุณ ถามตัวเองว่าจะ[firstname, surname].all?(&:blank?)เพิ่มอะไรนอกเหนือจากการทำให้คุณรู้สึกฉลาดเพราะมันแสดงทักษะของคุณแม้ว่าตอนนี้อ่านยากขึ้นบ้างไหม? ฉันจะบอกว่าตัวอย่างนี้น่าจะเต็มไปด้วยหมวดหมู่นั้น หากคุณเปรียบเทียบค่าห้าค่าฉันจะเห็นว่าเป็นรหัสที่ดี ดังนั้นอีกครั้งไม่มีเส้นสัมบูรณ์ที่นี่เพียงแค่ระวังการฉลาดเกินไป

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


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

1
@Sudiukil ไม่ใช่ผู้พัฒนาทับทิมฉันพบว่าการอ่านที่ยากที่สุดและไม่เหมาะกับสิ่งที่ฉันกำลังมองหา (จากมุมมองของภาษาอื่น) เป็นวิธีที่ "ดีที่สุด" อย่างไรก็ตามสำหรับบางคนที่คุ้นเคยกับความจริงที่ว่าทับทิมเป็นหนึ่งในภาษาเหล่านั้นที่ส่งคืนค่าของนิพจน์สุดท้ายมันน่าจะเป็นเวอร์ชั่นที่ง่ายที่สุดและง่ายที่สุดในการอ่าน อีกครั้งมันคือทั้งหมดที่เกี่ยวกับผู้ชมของคุณ
David Arno

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

โดยส่วนตัวฉันจะไปกับบล็อคโค้ดตัวที่สองของคุณยกเว้นฉันจะรวมสองประโยคในสาขาอื่นของคุณเป็นหนึ่ง:return "#{firstname} #{surname}".strip
Paul

2

นี่อาจเป็นคำถามที่ยากที่จะไม่ตอบคำถามตามความเห็น แต่นี่คือสองเซ็นต์ของฉัน

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

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


1

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

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

  • เคารพสำนวนของภาษา C #, Java, Ruby, Python ต่างก็มีวิธีที่ชอบในการทำสิ่งเดียวกัน โครงสร้างที่เป็นสำนวนช่วยให้เข้าใจรหัสที่คุณไม่คุ้นเคย
  • หยุดเมื่อรหัสของคุณอ่านง่ายขึ้นขึ้น ในตัวอย่างที่คุณระบุเกิดขึ้นเมื่อคุณกดรหัสลดคู่สุดท้าย คุณสูญเสียความได้เปรียบเชิงสำนวนของตัวอย่างก่อนหน้าและแนะนำสัญลักษณ์มากมายที่ต้องใช้ความคิดอย่างมากเพื่อทำความเข้าใจสิ่งที่เกิดขึ้นอย่างแท้จริง
  • ใช้ความคิดเห็นเฉพาะเมื่อคุณต้องแสดงให้เห็นถึงสิ่งที่ไม่คาดคิดใช้เพียงความเห็นเมื่อคุณมีการปรับบางสิ่งบางอย่างที่ไม่คาดคิดฉันรู้ว่าตัวอย่างของคุณอยู่ที่นั่นเพื่ออธิบายโครงสร้างให้ผู้คนคุ้นเคยกับ Ruby น้อยลงและนั่นก็โอเคสำหรับคำถาม ฉันต้องการใช้ความคิดเห็นเพื่ออธิบายกฎเกณฑ์ทางธุรกิจที่ไม่คาดคิดและหลีกเลี่ยงหากรหัสสามารถพูดได้

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

ฉันพูดทั้งหมดที่จะพูดแบบนี้:

ปรับปรุงเมื่อคุณสามารถทำให้โค้ดของคุณดีขึ้น (เข้าใจได้มากขึ้น)

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


0

ความสามารถในการอ่านเป็นคุณสมบัติที่คุณต้องการโดยที่ไม่ต้องมีหลายตัวเลือก ดังนั้นแทนที่จะเป็น "one-liners vs readability" คำถามควรจะเป็น:

หนึ่ง liners เพิ่มขึ้นอ่านง่ายและเมื่อพวกเขาทำอันตรายมัน

ฉันเชื่อว่าหนึ่ง liners เป็นสิ่งที่ดีสำหรับการอ่านเมื่อพวกเขาปฏิบัติตามสองเงื่อนไขนี้:

  1. มีความเฉพาะเจาะจงเกินกว่าจะถูกดึงไปยังฟังก์ชัน
  2. คุณไม่ต้องการขัดจังหวะ "การไหล" ของการอ่านโค้ดที่อยู่รอบ ๆ

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

puts "Name: #{user.email_if_there_is_no_name_otherwise_use_firstname_and_surname(use_email)}"

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

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

puts "Group: #{user.group}"
puts "Title: #{user.title}"
if user.firstname.blank? && user.surname.blank?) && use_email
  name = email
else
  name = "#{firstname} #{surname}"
  name.strip
end
puts "Name: #{name}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

โค้ด multiline นั้นสามารถอ่านได้ - แต่เมื่อคุณพยายามอ่านโค้ดที่อยู่รอบ ๆ (การพิมพ์ฟิลด์ต่าง ๆ ) ที่โครงสร้างหลายบรรทัดนั้นขัดขวางการไหล อ่านง่ายกว่า:

puts "Group: #{user.group}"
puts "Title: #{user.title}"
puts "Name: #{(email if (user.firstname.blank? && user.surname.blank?) && use_email) || "#{user.firstname} #{user.surname}".strip}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

การไหลของคุณไม่ถูกขัดจังหวะและคุณสามารถมุ่งเน้นไปที่การแสดงออกที่เฉพาะเจาะจงหากคุณต้องการ

นี่เป็นกรณีของคุณหรือไม่ ไม่อย่างแน่นอน!

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

สำหรับเงื่อนไขที่สอง - มันขัดจังหวะการไหลของรหัสโดยรอบหรือไม่? No! รหัสที่ล้อมรอบเป็นการประกาศวิธีการที่เลือกว่าnameมันเป็นวัตถุประสงค์เพียงอย่างเดียว ตรรกะของการเลือกชื่อไม่รบกวนการไหลของรหัสโดยรอบ - มันเป็นจุดประสงค์ที่สำคัญของรหัสโดยรอบ!

บทสรุป - อย่าทำให้ร่างกายของฟังก์ชั่นทั้งหมดเป็นหนึ่งซับ

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

บันทึก

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

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