พารามิเตอร์ที่มีชื่อทำให้รหัสอ่านง่ายขึ้นเขียนได้ยากขึ้น
เมื่อฉันกำลังอ่านโค้ดส่วนหนึ่งพารามิเตอร์ที่มีชื่อสามารถแนะนำบริบทที่ทำให้โค้ดเข้าใจง่ายขึ้น Color(1, 102, 205, 170)
พิจารณาเช่นคอนสตรัคนี้: นั่นหมายความว่าอย่างไรบนโลก? แน่นอนว่าColor(alpha: 1, red: 102, green: 205, blue: 170)
จะอ่านง่ายกว่ามาก แต่อนิจจาที่คอมไพเลอร์กล่าวว่า“ไม่” - Color(a: 1, r: 102, g: 205, b: 170)
มันต้องการ เมื่อเขียนโค้ดโดยใช้พารามิเตอร์ที่มีชื่อคุณใช้เวลาโดยไม่จำเป็นในการค้นหาชื่อที่ถูกต้องซึ่งจะง่ายกว่าที่จะลืมชื่อที่แน่นอนของพารามิเตอร์บางตัวมากกว่าที่จะลืมคำสั่งซื้อของพวกเขา
ครั้งนี้ฉันบิตเมื่อใช้DateTime
API ที่มีสองคลาสพี่น้องสำหรับคะแนนและระยะเวลากับอินเทอร์เฟซที่เหมือนกันเกือบ ในขณะที่DateTime->new(...)
ยอมรับการsecond => 30
โต้แย้งความDateTime::Duration->new(...)
ต้องการseconds => 30
และความคล้ายคลึงสำหรับหน่วยอื่น ๆ ใช่มันสมเหตุสมผลแล้ว แต่สิ่งนี้แสดงให้ฉันเห็นว่าการตั้งชื่อพารามิเตอร์ใช้งานง่าย
ชื่อที่ไม่ถูกต้องไม่ทำให้อ่านง่ายขึ้น
อีกตัวอย่างหนึ่งที่พารามิเตอร์ที่มีชื่อไม่ดีอาจเป็นภาษาR โค้ดส่วนนี้สร้างพล็อตข้อมูล:
plot(plotdata$n, plotdata$mu, type="p", pch=17, lty=1, bty="n", ann=FALSE, axes=FALSE)
คุณเห็นอาร์กิวเมนต์ตำแหน่งสองตำแหน่งสำหรับแถวข้อมูลxและyจากนั้นรายการพารามิเตอร์ที่มีชื่อ มีตัวเลือกเพิ่มเติมพร้อมค่าเริ่มต้นและมีเฉพาะรายการที่มีค่าเริ่มต้นที่ฉันต้องการเปลี่ยนหรือระบุอย่างชัดเจน เมื่อเราเพิกเฉยว่ารหัสนี้ใช้หมายเลขเวทย์มนตร์และอาจได้รับประโยชน์จากการใช้ enums (ถ้า R มีข้อใดข้อหนึ่ง!) ปัญหาคือชื่อพารามิเตอร์เหล่านี้ส่วนใหญ่จะอ่านไม่ออก
pch
อันที่จริงแล้วก็คือพล็อตตัวละคร, glyph ที่จะถูกดึงสำหรับทุกจุดข้อมูล 17
เป็นวงกลมที่ว่างเปล่าหรืออะไรทำนองนั้น
lty
เป็นประเภทเส้น นี่1
คือเส้นทึบ
bty
เป็นประเภทกล่อง การตั้งค่าให้"n"
หลีกเลี่ยงกล่องที่วาดรอบพล็อต
ann
ควบคุมลักษณะที่ปรากฏของคำอธิบายประกอบของแกน
สำหรับคนที่ไม่ทราบความหมายของตัวย่อแต่ละตัวเลือกเหล่านี้ค่อนข้างสับสน สิ่งนี้ยังแสดงให้เห็นว่าเพราะเหตุใด R จึงใช้ป้ายกำกับเหล่านี้: ไม่ใช่รหัสการจัดทำเอกสารด้วยตนเอง แต่ (เป็นภาษาที่พิมพ์แบบไดนามิก) เป็นกุญแจในการจับคู่ค่ากับตัวแปรที่ถูกต้อง
คุณสมบัติของพารามิเตอร์และลายเซ็น
ลายเซ็นฟังก์ชันอาจมีคุณสมบัติดังต่อไปนี้:
- อาร์กิวเมนต์สามารถสั่งซื้อหรือเรียงลำดับ
- ชื่อหรือไม่มีชื่อ
- จำเป็นหรือเป็นทางเลือก
- ลายเซ็นสามารถโอเวอร์โหลดได้ตามขนาดหรือประเภท
- และสามารถมีขนาดที่ไม่ระบุด้วย varargs
ภาษาที่ต่างกันมีพิกัดที่แตกต่างกันของระบบนี้ ใน C อาร์กิวเมนต์จะถูกจัดเรียงไม่มีชื่อจำเป็นต้องใช้เสมอและสามารถเป็น varargs ได้ ใน Java สถานการณ์คล้ายกันยกเว้นว่าลายเซ็นนั้นสามารถโอเวอร์โหลดได้ ใน Objective C ลายเซ็นจะได้รับคำสั่งชื่อจำเป็นและไม่สามารถโหลดได้มากเกินไปเพราะมันเป็นเพียงน้ำตาลรอบ ๆ C
ภาษาที่พิมพ์แบบไดนามิกด้วย varargs (อินเตอร์เฟสบรรทัดคำสั่ง, Perl, ... ) สามารถเลียนแบบพารามิเตอร์ที่มีชื่อเป็นตัวเลือก ภาษาที่มีการโหลดมากเกินไปขนาดลายเซ็นต์จะมีบางอย่างเช่นพารามิเตอร์ตัวเลือกเพิ่มเติม
วิธีที่จะไม่ใช้งานพารามิเตอร์ที่กำหนดชื่อ
เมื่อคิดถึงพารามิเตอร์ที่มีชื่อเรามักจะสมมติว่าพารามิเตอร์ที่มีชื่อเป็นตัวเลือกและไม่ได้เรียงลำดับ การใช้สิ่งเหล่านี้เป็นเรื่องยาก
พารามิเตอร์ทางเลือกอาจมีค่าเริ่มต้น สิ่งเหล่านี้ต้องถูกระบุโดยฟังก์ชันที่เรียกใช้และไม่ควรคอมไพล์ลงในรหัสการโทร มิฉะนั้นค่าเริ่มต้นจะไม่สามารถอัปเดตได้หากไม่มีการคอมไพล์โค้ดที่เกี่ยวข้องทั้งหมดใหม่
ตอนนี้คำถามที่สำคัญคือวิธีการส่งผ่านข้อโต้แย้งไปยังฟังก์ชั่น ด้วยพารามิเตอร์ที่สั่งให้ args สามารถส่งผ่านในรีจิสเตอร์หรือตามลำดับโดยธรรมชาติบนสแต็ก เมื่อเราแยกการลงทะเบียนครู่หนึ่งปัญหาคือวิธีการใส่อาร์กิวเมนต์ที่ไม่จำเป็นลงในสแต็ก
สำหรับสิ่งนั้นเราต้องการคำสั่งมากกว่าอาร์กิวเมนต์ที่เป็นตัวเลือก จะเกิดอะไรขึ้นถ้ารหัสการประกาศเปลี่ยนแปลง เนื่องจากคำสั่งไม่เกี่ยวข้องการเรียงลำดับใหม่ในการประกาศฟังก์ชันไม่ควรเปลี่ยนตำแหน่งของค่าในสแต็ก เราควรพิจารณาด้วยว่าการเพิ่มพารามิเตอร์ทางเลือกใหม่นั้นเป็นไปได้หรือไม่ จากมุมมองของผู้ใช้สิ่งนี้น่าจะเป็นเช่นนั้นเพราะรหัสที่ไม่ได้ใช้พารามิเตอร์นั้นก่อนหน้านี้ควรจะยังคงทำงานกับพารามิเตอร์ใหม่ได้ ดังนั้นสิ่งนี้จึงไม่รวมการเรียงลำดับเช่นการใช้คำสั่งในการประกาศหรือการใช้ลำดับตัวอักษร
พิจารณาสิ่งนี้ในแง่ของการพิมพ์ย่อยและหลักการทดแทน Liskov - ในผลลัพธ์ที่คอมไพล์คำสั่งเดียวกันควรจะสามารถเรียกใช้เมธอดบนชนิดย่อยที่มีพารามิเตอร์ที่มีชื่อใหม่และบน supertype
การใช้งานที่เป็นไปได้
หากเราไม่สามารถมีคำสั่งที่ชัดเจนดังนั้นเราจำเป็นต้องมีโครงสร้างข้อมูลที่ไม่มีการเรียงลำดับ
การนำไปใช้ที่ง่ายที่สุดคือการส่งชื่อพารามิเตอร์พร้อมกับค่าต่างๆ นี่คือการเลียนแบบชื่อที่ตั้งชื่อใน Perl หรือด้วยเครื่องมือบรรทัดคำสั่ง วิธีนี้จะช่วยแก้ปัญหาส่วนขยายทั้งหมดที่กล่าวถึงข้างต้น แต่อาจเป็นการสิ้นเปลืองพื้นที่ขนาดใหญ่ไม่ใช่ตัวเลือกในโค้ดที่จำเป็นต่อประสิทธิภาพ นอกจากนี้การประมวลผลพารามิเตอร์ที่มีชื่อเหล่านี้ก็มีความซับซ้อนมากกว่าเพียงแค่ดึงค่าออกจากสแต็ก
ที่จริงแล้วความต้องการพื้นที่สามารถลดลงได้โดยใช้การรวมสตริงซึ่งสามารถลดการเปรียบเทียบสตริงในภายหลังเพื่อเปรียบเทียบตัวชี้ (ยกเว้นเมื่อไม่สามารถรับประกันได้ว่าสตริงสแตติกจะถูกพูลจริง ๆ แล้วในกรณีนี้ทั้งสองสตริงจะต้องเปรียบเทียบ ดูรายละเอียด)
แต่เราสามารถส่งผ่านโครงสร้างข้อมูลที่ชาญฉลาดซึ่งทำงานเป็นพจนานุกรมของอาร์กิวเมนต์ที่มีชื่อ สิ่งนี้มีราคาถูกทางด้านผู้โทรเนื่องจากชุดของคีย์เป็นที่รู้จักกันในตำแหน่งการโทร สิ่งนี้จะอนุญาตให้สร้างฟังก์ชันแฮชที่สมบูรณ์แบบหรือเพื่อคำนวณคู่ชีวิตใหม่ ผู้รับสายจะยังคงต้องทดสอบการมีอยู่ของชื่อพารามิเตอร์ที่เป็นไปได้ทั้งหมดซึ่งค่อนข้างแพง Python ใช้สิ่งนี้เช่นนี้
ดังนั้นในกรณีส่วนใหญ่ก็มีราคาแพงเกินไป
หากฟังก์ชั่นที่มีพารามิเตอร์ที่ระบุชื่อนั้นสามารถขยายได้อย่างเหมาะสมจะไม่สามารถสันนิษฐานได้ว่าการเรียงลำดับขั้นสุดท้าย ดังนั้นจึงมีเพียงสองวิธี:
- ทำให้คำสั่งของชื่อ params เป็นส่วนหนึ่งของลายเซ็นและไม่อนุญาตในภายหลังการเปลี่ยนแปลง สิ่งนี้มีประโยชน์สำหรับรหัสการทำเอกสารด้วยตัวเอง แต่ไม่ช่วยให้มีอาร์กิวเมนต์ที่เป็นทางเลือก
- ส่งผ่านโครงสร้างข้อมูลคีย์ - ค่าไปยังผู้รับซึ่งจะต้องดึงข้อมูลที่เป็นประโยชน์ การเปรียบเทียบนี้มีราคาแพงมากและมักจะเห็นเฉพาะในภาษาสคริปต์โดยไม่เน้นประสิทธิภาพ
ข้อผิดพลาดอื่น ๆ
ชื่อตัวแปรในการประกาศฟังก์ชั่นมักจะมีความหมายภายในและไม่ได้เป็นส่วนหนึ่งของอินเทอร์เฟซแม้ว่าเครื่องมือเอกสารจำนวนมากจะยังคงแสดงให้พวกเขา ในหลายกรณีคุณต้องการชื่อที่แตกต่างกันสำหรับตัวแปรภายในและอาร์กิวเมนต์ที่มีชื่อที่เกี่ยวข้อง ภาษาที่ไม่อนุญาตให้เลือกชื่อภายนอกที่มองเห็นได้ของพารามิเตอร์ที่กำหนดชื่อนั้นจะไม่ได้รับมากนักหากไม่ได้ใช้ชื่อตัวแปรกับบริบทการโทร
ปัญหาเกี่ยวกับการจำลองของอาร์กิวเมนต์ที่มีชื่อคือการขาดการตรวจสอบแบบคงที่ในด้านของผู้โทร นี่เป็นเรื่องง่ายโดยเฉพาะอย่างยิ่งที่จะลืมเมื่อผ่านพจนานุกรมของข้อโต้แย้ง (มองคุณ Python) นี้เป็นสิ่งสำคัญเพราะผ่านพจนานุกรมเป็นวิธีแก้ปัญหาที่พบบ่อยเช่นใน foo({bar: "baz", qux: 42})
JavaScript: ที่นี่ทั้งประเภทของค่าหรือการมีอยู่หรือไม่มีชื่อบางอย่างสามารถตรวจสอบแบบคงที่
การจำลองชื่อพารามิเตอร์ (ในภาษาที่พิมพ์แบบคงที่)
เพียงใช้สตริงเป็นคีย์และวัตถุใด ๆ ที่เป็นค่าจะไม่มีประโยชน์มากในการมีตัวตรวจสอบชนิดสแตติก อย่างไรก็ตามอาร์กิวเมนต์ที่ตั้งชื่อสามารถเลียนแบบด้วย structs หรือตัวอักษรของวัตถุ:
// Java
static abstract class Arguments {
public String bar = "default";
public int qux = 0;
}
void foo(Arguments args) {
...
}
/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});