เทคนิคการลดจำนวนอาร์กิวเมนต์ของฟังก์ชันให้น้อยที่สุด


13

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

วิธีหนึ่งคือการแยกอาร์กิวเมนต์ไปยังคลาสใหม่ แต่นั่นจะนำไปสู่การระเบิดของคลาสอย่างแน่นอนหรือไม่ และคลาสเหล่านั้นมีแนวโน้มที่จะจบลงด้วยชื่อที่ละเมิดกฎการตั้งชื่อบางอย่าง (ลงท้ายด้วย "Data" หรือ "Info" ฯลฯ ) หรือไม่

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

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


21
ฉันไม่เห็นด้วยกับรหัสที่สะอาดเลย หากจำนวนอาร์กิวเมนต์ของฟังก์ชันเป็นศูนย์แสดงว่าฟังก์ชันนั้นมีผลข้างเคียงและอาจมีการเปลี่ยนแปลงสถานะบางแห่ง ในขณะที่ฉันยอมรับว่าข้อโต้แย้งน้อยกว่า 4 อาจเป็นกฎง่ายๆ - ฉันอยากจะมีฟังก์ชั่นที่มี 8 ข้อโต้แย้งที่เป็นแบบคงที่และไม่มีผลข้างเคียงมากกว่าฟังก์ชั่นแบบไม่คงที่กับข้อโต้แย้งเป็นศูนย์ที่เปลี่ยนสถานะและมีผลข้างเคียง .
wasatz

4
" ใน Clean Code มันเขียนว่า" จำนวนอาร์กิวเมนต์ที่เหมาะสมที่สุดสำหรับฟังก์ชั่นเป็นศูนย์ " " จริงเหรอ? มันผิดมาก! จำนวนพารามิเตอร์ที่เหมาะสมที่สุดคือพารามิเตอร์หนึ่งที่มีค่าส่งคืนที่ได้มาจากพารามิเตอร์หนึ่งนั้น ในทางปฏิบัติจำนวนพารามิเตอร์ไม่สำคัญมาก สิ่งที่สำคัญคือเมื่อใดก็ตามที่เป็นไปได้ฟังก์ชั่นควรบริสุทธิ์ (กล่าวคือมันจะได้รับผลตอบแทนจากพารามิเตอร์เท่านั้นโดยไม่มีผลข้างเคียง)
David Arno

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

2
เป็นไปได้ที่ซ้ำกันของกลยุทธ์สำหรับการตัดพารามิเตอร์
ริ้น

คำตอบ:


16

สิ่งที่สำคัญที่สุดที่ควรจดจำคือสิ่งเหล่านี้เป็นแนวทางไม่ใช่กฎ

มีหลายกรณีที่วิธีการนั้นจะต้องมีการโต้แย้ง ลองนึกถึง+วิธีการหาตัวเลขเช่น หรือaddวิธีการรวบรวม

ในความเป็นจริงเราอาจโต้แย้งว่าการเพิ่มสองตัวเลขนั้นขึ้นอยู่กับบริบทเช่นในℤ 3 + 3 == 6แต่ในℤ | 5 3 + 3 == 2ดังนั้นผู้ดำเนินการเพิ่มเติมควรเป็นวิธีการในวัตถุบริบทที่รับสองอาร์กิวเมนต์แทน วิธีการเกี่ยวกับตัวเลขที่ใช้เวลาหนึ่งอาร์กิวเมนต์

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

ที่กล่าวว่ามีสองสามสิ่งที่สามารถทำได้เพื่อลดจำนวนอาร์กิวเมนต์สำหรับวิธีการ:

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

วิธีหนึ่งคือการแยกอาร์กิวเมนต์ไปยังคลาสใหม่ แต่นั่นจะนำไปสู่การระเบิดของคลาสอย่างแน่นอนหรือไม่

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

และคลาสเหล่านั้นมีแนวโน้มที่จะจบลงด้วยชื่อที่ละเมิดกฎการตั้งชื่อบางอย่าง (ลงท้ายด้วย "Data" หรือ "Info" ฯลฯ ) หรือไม่

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

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

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

สังเกตว่าฉันใช้คำว่า "อาจจะ" บ่อยแค่ไหน? นั่นเป็นสาเหตุที่เป็นแนวทางไม่ใช่กฎ บางทีวิธีการของคุณที่มี 4 พารามิเตอร์นั้นดีมาก!


7
@ BrunoSchäpper: Sure: (1) " ให้เป็นวิธีการของตัวเองที่มีขนาดเล็ก: บางทีถ้าความต้องการวิธีการที่ข้อโต้แย้งจำนวนมากก็จะทำมากเกินไป " จำนวนพารามิเตอร์เป็นการทดสอบที่ไม่ดี ตัวเลือก / บูลีนพารามิเตอร์และบรรทัดจำนวนมากเป็นตัวบ่งชี้ที่แข็งแกร่งของวิธีการที่ทำมากเกินไป หลายคนมีความอ่อนแอที่สุด (2) " สถานะของวัตถุ: หากต้องการอาร์กิวเมนต์หลายวิธีอาจจะเป็นส่วนหนึ่งของสถานะวัตถุ " ไม่ไม่สามครั้งไม่ ลดสถานะวัตถุ ไม่ใช่ฟังก์ชั่นพารามิเตอร์ ถ้าเป็นไปได้ส่งผ่านค่าไปยังวิธีการทั้งหมดผ่านพารามิเตอร์เพื่อหลีกเลี่ยงสถานะวัตถุ
David Arno

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

ขอบคุณ @DavidArno 1) ตกลงไม่ใช่ตัวบ่งชี้ที่แข็งแกร่งในและของตัวเอง แต่เป็นสิ่งที่ดีอย่างไรก็ตาม ฉันมักจะเห็นวิธีการสองสามอย่างโดยที่วัตถุบางอย่างผ่านไปอย่างไม่ต่อเนื่อง ไม่มีสถานะวัตถุ นั่นเป็นสิ่งที่ดี แต่ 2) ตัวเลือกที่ดีกว่า IMHO กำลังปรับวิธีการเหล่านั้นใหม่ให้ย้ายสถานะโดยนัยไปยังคลาสใหม่ซึ่งใช้พารามิเตอร์เหล่านี้ทั้งหมดเป็นอาร์กิวเมนต์ที่ชัดเจน คุณจบลงด้วยวิธีการโต้แย้งศูนย์สาธารณะหนึ่งวิธีและวิธีการภายในการโต้แย้งศูนย์ต่อหนึ่งจำนวนมาก รัฐไม่ใช่สาธารณะสากลหรือแม้กระทั่งมีชีวิตอยู่ได้นาน แต่รหัสนั้นสะอาดกว่ามาก
Bruno Schäpper

6

โปรดทราบว่าศูนย์ข้อโต้แย้งไม่ได้บ่งบอกถึงผลข้างเคียงเพราะวัตถุของคุณเป็นข้อโต้แย้งโดยปริยาย ดูวิธีการหลายวิธีการศูนย์ arity รายการเปลี่ยนรูป Scala ของมีตัวอย่างเช่น

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

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

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

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


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

0

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

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

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

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


5
แน่นอนว่าการลดจำนวนข้อโต้แย้งเป็นสิ่งที่ผิดแน่นอน แต่ฉันไม่เห็นด้วยกับ "ไม่มีอะไรผิดปกติกับฟังก์ชั่นที่มีข้อโต้แย้งจำนวนมาก" . ในความคิดของฉันเมื่อคุณกดฟังก์ชั่นที่มีอาร์กิวเมนต์จำนวนมากใน 99,9% ของทุกกรณีมีวิธีการปรับปรุงโครงสร้างของโค้ดในลักษณะที่จงใจซึ่ง (เช่นกัน) จะลดจำนวนอาร์กิวเมนต์ของฟังก์ชัน
Doc Brown

@DocBrown นั่นทำไมมีย่อหน้าสุดท้ายนี้และคำแนะนำในการเริ่มต้นมี .... และอีกคนหนึ่ง: คุณอาจจะไม่เคยพยายามที่จะโปรแกรมกับ MS Windows API;)
tofro

"บางทีฟังก์ชั่นที่ต้องการพารามิเตอร์จำนวนมากกำลังพยายามทำมากเกินไปและควรแบ่งออกเป็นฟังก์ชั่นเล็ก ๆ หลายตัว" ฉันเห็นด้วยอย่างสิ้นเชิงแม้ว่าในทางปฏิบัติแล้วคุณไม่ได้จบลงด้วยฟังก์ชั่นอื่นที่สูงกว่าสแต็กที่เรียกฟังก์ชั่นเล็ก ๆ เหล่านั้นบ้างไหม? จากนั้นคุณสามารถ refactor พวกเขาเป็นวัตถุ แต่วัตถุนั้นจะมี ctor คุณสามารถใช้ผู้สร้าง blah blah blah ประเด็นก็คือมันเป็นการถดถอยที่ไม่สิ้นสุด - ที่ใดที่หนึ่งมีจำนวนของค่าที่จำเป็นสำหรับซอฟต์แวร์ที่จะทำงานและพวกเขาจะต้องได้รับฟังก์ชั่นเหล่านั้นอย่างใด
Neil Barnwell

1
@NeilBarnwell ในกรณีที่เหมาะสมที่สุด (มูลค่าการปรับโครงสร้างใหม่) คุณอาจสามารถแยกฟังก์ชั่นที่ต้องการอาร์กิวเมนต์ 10 ข้อเป็นสามข้อที่ต้องใช้อาร์กิวเมนต์ 3-4 ข้อ ในกรณีที่ไม่เหมาะคุณจบด้วยฟังก์ชั่นสามฟังก์ชั่นที่ต้องการอาร์กิวเมนต์ 10 ข้อ (ควรทิ้งไว้คนเดียว) สำหรับอาร์กิวเมนต์ของสแต็กที่สูงขึ้นของคุณ: เห็นด้วย - อาจเป็นกรณี แต่ไม่จำเป็น - อาร์กิวเมนต์มาจากที่ใดที่หนึ่งและการดึงข้อมูลนั้นอาจอยู่ในสแต็กเช่นกัน มีแนวโน้มที่จะแตกต่างกันไป
tofro

ตรรกะของซอฟต์แวร์ไม่ต้องการพารามิเตอร์มากกว่าสี่ตัว คอมไพเลอร์เท่านั้นที่อาจ
หมอ

0

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

ข้อยกเว้นเพียงอย่างเดียวคือคอมไพเลอร์ (metaprograms) และแบบจำลองโดยที่ค่า จำกัด นั้นเป็นไปตามทฤษฎี 8 แต่อาจเป็นเพียง 5 เท่านั้น

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