มีแนวทางว่าควรยอมรับฟังก์ชั่นพารามิเตอร์จำนวนเท่าใด?


114

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

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


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

2
ไม่ทำตามตัวอย่างของMPI_Sendrecv ()ซึ่งใช้พารามิเตอร์ 12 ตัวแน่นอน!
chrisaycock

6
โครงการที่ฉันกำลังใช้อยู่นั้นใช้เฟรมเวิร์กซึ่งวิธีการที่มีพารามิเตอร์มากกว่า 10+ นั้นเป็นเรื่องธรรมดา ฉันกำลังเรียกวิธีการหนึ่งโดยเฉพาะกับ 27 พารามิเตอร์ในสองแห่ง ทุกครั้งที่ฉันเห็นมันฉันตายข้างในเล็กน้อย
per

3
อย่าเพิ่มสวิตช์บูลีนเพื่อเปลี่ยนพฤติกรรมของฟังก์ชั่น แยกฟังก์ชั่นแทน แยกพฤติกรรมทั่วไปออกเป็นฟังก์ชั่นใหม่
kevin cline

2
@Ominus คืออะไร? มีเพียง 10 พารามิเตอร์เท่านั้น? นั่นเป็นอะไรมากขึ้นเป็นสิ่งจำเป็น : D
maaartinus

คำตอบ:


106

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

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

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

มีประโยชน์ที่จะได้รับจากการทำสิ่งนี้:

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

5
สิ่งที่เป็นนามธรรมได้กลายเป็นรูปแบบการออกแบบหรือไม่? จะเกิดอะไรขึ้นถ้าคุณมีคลาสพารามิเตอร์ 3 คลาส คุณเพิ่มวิธีโอเวอร์โหลดอีก 9 วิธีเพื่อจัดการกับการรวมกันของพารามิเตอร์ต่าง ๆ ที่เป็นไปได้หรือไม่? ฟังดูเหมือนปัญหาการประกาศพารามิเตอร์ O (n ^ 2) ที่น่ารังเกียจ โอ้เดี๋ยวก่อนคุณสามารถรับ 1 คลาสใน Java / C # เท่านั้นซึ่งจะต้องใช้แผ่น biolerplate เพิ่มเติม (อาจมีคลาสย่อยมากกว่า) เพื่อให้สามารถใช้งานได้จริง ขออภัยฉันไม่มั่นใจ การเพิกเฉยต่อวิธีการแสดงออกที่ชัดเจนกว่าภาษาอาจให้ความซับซ้อนเพียงแค่รู้สึกผิด
Evan Plaice

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

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

@MichaelK หากคุณไม่เคยใช้ให้ลอง googling 'object initializers' มันเป็นวิธีการที่ค่อนข้างแปลกใหม่ที่ช่วยลดความผิดเพี้ยนไปอย่างมาก ในทางทฤษฎีคุณสามารถกำจัดคลาสคอนสตรัคเตอร์พารามิเตอร์และโอเวอร์โหลดได้ทั้งหมดในนัดเดียว แม้ว่าในทางปฏิบัติมันเป็นเรื่องปกติที่จะรักษาคอนสตรัคเตอร์ร่วมกันไว้หนึ่งตัวและอาศัยไวยากรณ์ 'object initializer' สำหรับคุณสมบัติอื่น ๆ IMHO มันใกล้เคียงที่สุดที่คุณจะได้รับการแสดงออกของภาษาที่พิมพ์แบบไดนามิกในภาษาที่พิมพ์แบบคงที่
Evan Plaice

@Evain Plaice: ตั้งแต่เมื่อใดภาษาที่พิมพ์แบบไดนามิกแสดงออกมา?
ThomasX

41

ตาม "รหัสสะอาด: คู่มือของงานฝีมือซอฟต์แวร์ Agile" ศูนย์เป็นอุดมคติหนึ่งหรือสองเป็นที่ยอมรับสามในกรณีพิเศษและสี่หรือมากกว่าไม่เคย!

คำพูดของผู้เขียน:

จำนวนอาร์กิวเมนต์ที่เหมาะสมที่สุดสำหรับฟังก์ชันคือศูนย์ (niladic) ถัดมาคือหนึ่ง (monadic) ตามมาด้วยสอง (dyadic) ควรหลีกเลี่ยงการโต้เถียงสามครั้ง (triadic) หากเป็นไปได้ มากกว่าสาม (polyadic) ต้องมีเหตุผลพิเศษมาก - จากนั้นไม่ควรใช้ต่อไป

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

ในความเห็นส่วนตัวของฉันพารามิเตอร์หนึ่งดีกว่าไม่มีใครเพราะฉันคิดว่าชัดเจนกว่าสิ่งที่เกิดขึ้น

ตัวอย่างในความคิดของฉันตัวเลือกที่สองดีกว่าเพราะชัดเจนกว่าวิธีการประมวลผล:

LangDetector detector = new LangDetector(someText);
//lots of lines
String language = detector.detectLanguage();

เมื่อเทียบกับ

LangDetector detector = new LangDetector();
//lots of lines
String language = detector.detectLanguage(someText);

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


8
"สามในกรณีพิเศษและสี่หรือมากกว่านั้นไม่เคย!" BS วิธีการเกี่ยวกับ Matrix.Create (x1, x2, x3, x4, x5, x6, x7, x8, x9); ?
Lukasz Madon

71
ศูนย์เหมาะที่สุด? heck ทำหน้าที่อย่างไรในการรับข้อมูล? Global / instance / static / ตัวแปรใด? yuck
Peter C

9
นั่นเป็นตัวอย่างที่ไม่ดี คำตอบนั้นชัดเจน: String language = detectLanguage(someText);. ในกรณีใดกรณีหนึ่งของคุณที่คุณผ่านการโต้แย้งจำนวนเท่ากันมันก็เกิดขึ้นว่าคุณแยกการทำงานของฟังก์ชั่นเป็นสองเนื่องจากภาษาที่ไม่ดี
Matthieu M.

8
@lukas ในภาษาที่สนับสนุนการสร้างแฟนซีเช่นอาร์เรย์หรือ (อ้าปากค้าง!) รายการเกี่ยวกับวิธีการMatrix.Create(input);ที่inputเป็นพูด, .NET IEnumerable<SomeAppropriateType>? ด้วยวิธีนี้คุณไม่จำเป็นต้องมีการโอเวอร์โหลดแยกต่างหากเมื่อคุณต้องการสร้างเมทริกซ์ที่ถือ 10 องค์ประกอบแทน 9
CVn

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

24

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

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

หากคุณทำโมเดลถูกต้อง

3rdGradeClass.finishHomework(int lessonId) {
    result = students.assignHomework(lessonId, dueDate);
    teacher.verifyHomeWork(result);
}

มันง่ายมาก

หากคุณไม่มีรูปแบบที่ถูกต้องวิธีการจะเป็นเช่นนี้

Manager.finishHomework(grade, students, lessonId, teacher, ...) {
    // This is not good.
}

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

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

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


16

แง่มุมหนึ่งที่คำตอบอื่น ๆ ไม่ตอบคือประสิทธิภาพ

หากคุณกำลังเขียนโปรแกรมในภาษาระดับต่ำอย่างเพียงพอ (C, C ++, ชุดประกอบ) พารามิเตอร์จำนวนมากอาจเป็นอันตรายต่อประสิทธิภาพการทำงานในสถาปัตยกรรมบางอย่างโดยเฉพาะอย่างยิ่งหากฟังก์ชันเรียกว่าจำนวนครั้งมาก

เมื่อมีสายเรียกฟังก์ชั่นจะทำใน ARM เช่นสี่ข้อโต้แย้งแรกจะถูกวางไว้ในทะเบียนr0การr3และข้อโต้แย้งที่เหลือจะต้องมีการผลักดันให้เข้าสู่สแต็ค การรักษาจำนวนอาร์กิวเมนต์ให้ต่ำกว่าห้าสามารถสร้างความแตกต่างสำหรับฟังก์ชั่นที่สำคัญ

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

ปรับปรุง:

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

มีปัญหาบางอย่างกับ inlining แม้ว่า

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

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

อินไลน์ไม่สามารถแก้ปัญหานี้ได้หรือ
KjMag

@KjMag: ใช่ในระดับหนึ่ง แต่ก็มี gotchas มากมายขึ้นอยู่กับคอมไพเลอร์ โดยปกติฟังก์ชั่นจะถูกแทรกเข้าไปในระดับหนึ่งเท่านั้น (ถ้าคุณเรียกใช้ฟังก์ชั่นอินไลน์ที่เรียกฟังก์ชั่นอินไลน์ที่เรียกฟังก์ชั่นอินไลน์ .... ) หากฟังก์ชั่นมีขนาดใหญ่และถูกเรียกจากสถานที่จำนวนมากการฝังทุกที่ทำให้ไบนารีใหญ่ขึ้นซึ่งอาจทำให้คิดถึง I-cache มากขึ้น ดังนั้นการทำ inline สามารถช่วยได้ แต่มันไม่มี bullet เงิน (ไม่ต้องพูดถึงมีคอมไพเลอร์แบบฝังตัวบางตัวที่ไม่รองรับinline) ค่อนข้างมาก
Leo

7

เมื่อรายการพารามิเตอร์เติบโตเกินห้าให้พิจารณากำหนดโครงสร้างหรือวัตถุ "บริบท"

นี่เป็นเพียงโครงสร้างที่เก็บพารามิเตอร์ทางเลือกทั้งหมดที่มีค่าเริ่มต้นที่สมเหตุสมผล

ในโลกแห่งกระบวนการซีโครงสร้างแบบธรรมดาจะทำ ใน Java, C ++ วัตถุง่าย ๆ จะพอเพียง อย่ายุ่งกับผู้ได้รับหรือผู้ตั้งค่าเพราะวัตถุประสงค์เพียงอย่างเดียวของวัตถุคือการเก็บค่าที่ตั้งค่าได้ "สาธารณะ"


ฉันยอมรับวัตถุบริบทสามารถกลายเป็นประโยชน์อย่างยิ่งเมื่อโครงสร้างพารามิเตอร์ฟังก์ชันเริ่มกลายเป็นค่อนข้างซับซ้อน ฉันเพิ่ง blogged เกี่ยวกับการใช้วัตถุบริบทที่มีรูปแบบเหมือนผู้เข้าชม
Lukas Eder

5

ไม่ไม่มีหลักเกณฑ์มาตรฐาน

แต่มีเทคนิคบางอย่างที่สามารถทำให้ฟังก์ชั่นที่มีพารามิเตอร์จำนวนมากสามารถรับได้

คุณสามารถใช้พารามิเตอร์ list-if-args (args *) หรือพารามิเตอร์ dictionary-of-args (kwargs **)

ตัวอย่างเช่นในไพ ธ อน:

// Example definition
def example_function(normalParam, args*, kwargs**):
  for i in args:
    print 'args' + i + ': ' + args[i] 
  for key in kwargs:
    print 'keyword: %s: %s' % (key, kwargs[key])
  somevar = kwargs.get('somevar','found')
  missingvar = kwargs.get('somevar','missing')
  print somevar
  print missingvar

// Example usage

    example_function('normal parameter', 'args1', args2, 
                      somevar='value', missingvar='novalue')

ขาออก:

args1
args2
somevar:value
someothervar:novalue
value
missing

หรือคุณสามารถใช้ไวยากรณ์คำจำกัดความตัวอักษร

ตัวอย่างเช่นต่อไปนี้เป็นการเรียก JavaScript jQuery เพื่อเปิดใช้งานคำขอ AJAX GET:

$.ajax({
  type: 'GET',
  url: 'http://someurl.com/feed',
  data: data,
  success: success(),
  error: error(),
  complete: complete(),
  dataType: 'jsonp'
});

หากคุณดูที่คลาส ajax ของ jQuery มีคุณสมบัติเพิ่มเติมมากมาย (ประมาณ 30) คุณสมบัติที่สามารถตั้งค่าได้ ส่วนใหญ่เป็นเพราะการสื่อสาร Ajax มีความซับซ้อนมาก โชคดีที่ไวยากรณ์ตัวอักษรของวัตถุทำให้ชีวิตง่ายขึ้น


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

ภาษาที่พิมพ์แบบไดนามิกเช่น python / javascript ไม่มีความสามารถดังกล่าวดังนั้นจึงเป็นเรื่องธรรมดามากที่จะเห็นอาร์กิวเมนต์ของคำหลักและคำจำกัดความตามตัวอักษรของวัตถุ

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

IMHO วิธีการใช้งานมากเกินไปจะประเมินค่าสูงเกินไป

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


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

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

ปรับปรุง:

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

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


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

@MichaelK ลองดูที่การเริ่มต้นวัตถุอีกครั้ง พวกเขาช่วยให้คุณสามารถกำหนดคุณสมบัติอย่างชัดเจนเมื่อเทียบกับวิธีที่พวกเขากำหนดไว้โดยนัยในพารามิเตอร์วิธีการ / ฟังก์ชั่นแบบดั้งเดิม อ่านสิ่งนี้msdn.microsoft.com/en-us/library/bb397680.aspxเพื่อดูว่าฉันหมายถึงอะไร
Evan Plaice

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

@Telastyn คุณกำลังพูดถึงอะไร? ไม่มีการสร้างชนิดใหม่คุณประกาศคุณสมบัติโดยตรงโดยใช้ไวยากรณ์ตัวอักษรของวัตถุ มันเหมือนกับการกำหนดวัตถุที่ไม่ระบุชื่อ แต่วิธีการตีความมันเป็นการจัดกลุ่มพารามิเตอร์ key = value สิ่งที่คุณกำลังดูคือการสร้างอินสแตนซ์เมธอด (ไม่ใช่พารามิเตอร์การห่อหุ้มวัตถุ) หากเนื้อวัวของคุณอยู่ในบรรจุภัณฑ์พารามิเตอร์ให้ดูที่รูปแบบวัตถุพารามิเตอร์ที่กล่าวถึงในหนึ่งในคำถามอื่น ๆ เพราะนั่นคือสิ่งที่มันเป็น
Evan Plaice

@EvanPlaice - ยกเว้นว่าภาษาการเขียนโปรแกรมแบบคงที่โดยใหญ่แล้วต้องการประเภทประกาศ (มักจะใหม่) เพื่อให้รูปแบบวัตถุพารามิเตอร์
Telastyn

3

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

แต่อีกครั้งนั่นเป็นแนวทางมากกว่ากฎ ฉันมักจะมีฟังก์ชั่นที่ใช้พารามิเตอร์มากกว่าสองตัวเนื่องจากสถานการณ์ที่ผิดปกติหรือใช้งานง่าย


2

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

ดังนั้นแทนที่จะเป็น (ตัวอย่าง) สิ่งนี้:

<?php

createBlogPost('the title', 'the summary', 'the author', 'the date of publication, 'the keywords', 'the category', 'etc');

?>

ไปสำหรับ:

<?php

// create a hash of post data
$post_data = array(
  'title'    => 'the title',
  'summary'  => 'the summary',
  'author'   => 'the author',
  'pubdate'  => 'the publication date',
  'keywords' => 'the keywords',
  'category' => 'the category',
  'etc'      => 'etc',
);

// and pass it to the appropriate function
createBlogPost($post_data);

?>

Wordpress ทำสิ่งต่าง ๆ มากมายด้วยวิธีนี้และฉันคิดว่ามันใช้งานได้ดี (แม้ว่าโค้ดตัวอย่างของฉันด้านบนเป็นจินตภาพและไม่ได้เป็นตัวอย่างจาก Wordpress)

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

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

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


การเห็นโพสต์นี้เป็นการเน้นถึงประโยชน์อีกประการของวิธี Pass-a-hash: โปรดสังเกตว่าตัวอย่างรหัสแรกของฉันมันยาวมากที่จะสร้างแถบเลื่อน อาจเป็นเช่นเดียวกันในโปรแกรมแก้ไขรหัสของคุณ
Chris Allen Lane

0

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

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

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


0

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

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

สำหรับincludeSetupPage()ตัวอย่างของเขาด้านบนนี่เป็นตัวอย่างเล็กน้อยของ refactored "รหัสสะอาด" ของเขาในตอนท้ายของบท:

// *** NOTE: Commments are mine, not the author's ***
//
// Java example
public class SetupTeardownIncluder {
    private StringBuffer newPageContent;

    // [...] (skipped over 4 other instance variables and many very small functions)

    // this is the zero-argument function in the example,
    // which calls a method that eventually uses the StringBuffer instance variable
    private void includeSetupPage() throws Exception {
        include("SetUp", "-setup");
    }

    private void include(String pageName, String arg) throws Exception {
        WikiPage inheritedPage = findInheritedPage(pageName);
        if (inheritedPage != null) {
            String pagePathName = getPathNameForPage(inheritedPage);
            buildIncludeDirective(pagePathName, arg);
        }
    }

    private void buildIncludeDirective(String pagePathName, String arg) {
        newPageContent
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n");
    }
}

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

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


-2

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

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

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

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


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

4
-1 เพื่อไม่อ้างอิงแหล่งที่มา
Joshua Drake

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

1
ดูการโต้เถียงของลุงบ็อบที่: informit.com/articles/article.aspx?p=1375308และสังเกตว่าที่ด้านล่างเขาบอกว่า "ฟังก์ชั่นควรมีอาร์กิวเมนต์จำนวนน้อยไม่มีอาร์กิวเมนต์ดีที่สุดตามด้วยหนึ่งสองและสาม มากกว่าสามข้อสงสัยมากและควรหลีกเลี่ยงด้วยอคติ
Michael Durrant

ฉันได้รับแหล่งที่มา ตลกไม่มีความคิดเห็นตั้งแต่นั้นมา ฉันยังได้พยายามที่จะตอบส่วน 'แนวทาง' ที่หลายคนคิดว่าลุงบ๊อบและรหัสสะอาดเป็นแนวทาง ที่น่าสนใจว่าคำตอบยอดนิยมอย่างไม่น่าเชื่อ upvote (ปัจจุบัน) กล่าวว่าไม่ทราบถึงแนวทางใด ๆ ลุงบ๊อบไม่ได้ตั้งใจที่จะเป็นผู้มีอำนาจ แต่มันค่อนข้างจะเป็นและคำตอบนี้อย่างน้อยก็พยายามที่จะตอบคำถามเฉพาะของคำถาม
Michael Durrant
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.