ข้ามไปยังเนื้อหา

Parameterize Function

มองหา function ตั้งแต่สองตัวขึ้นไปที่ body เหมือนกันหมด ต่างกันแค่ค่า hard-code ค่าเดียว แล้วยุบให้เหลือ function เดียวที่รับค่านั้นเป็น parameter จากนั้น logic ที่เคยซ้ำจะอยู่ที่เดียวเป๊ะ ๆ และความต่างระหว่างเวอร์ชันเดิมก็กลายเป็น argument ที่มองเห็นได้ตรงจุดที่เรียก

นี่คือ Duplicated Code อีกรูปแบบหนึ่ง คุณจะเห็น discountFivePercent กับ discountTenPercent นั่งติดกัน เหมือนกันทุกอย่างยกเว้นตัวเลข หรือ tierBronze กับ tierSilver ที่ต่างกันแค่ค่า threshold ทุกครั้งที่ต้องแก้ logic ส่วนที่ใช้ร่วมกัน คุณต้องจำเองว่าต้องไล่แก้ทุก clone แล้ววันหนึ่งก็จะลืมไปสักตัว ในเมื่อค่า hard-code คือสิ่งเดียวที่ต่างกัน ค่านั้นก็ควรเป็น argument ไม่ใช่เหตุผลให้คัดลอกทั้ง function

function สองตัวที่คำนวณราคาหลังหักส่วนลด ต่างกันแค่อัตรา หลังจากนั้น function เดียวรับอัตราเป็น parameter

// Before
function priceWithFivePercentOff(price: number): number {
return price - price * 0.05;
}
function priceWithTenPercentOff(price: number): number {
return price - price * 0.10;
}
const a = priceWithFivePercentOff(200);
const b = priceWithTenPercentOff(200);
// After
function discountedPrice(price: number, rate: number): number {
return price - price * rate;
}
const a = discountedPrice(200, 0.05);
const b = discountedPrice(200, 0.10);
  1. เลือก function ที่เกือบเหมือนกันตัวหนึ่งมาเป็นแม่แบบ
  2. เพิ่ม parameter สำหรับค่าที่แปรเปลี่ยน ตั้งชื่อให้สื่อความหมายอย่าง rate หรือ threshold ไม่ใช่ value
  3. ใน body ให้เปลี่ยนค่า hard-code เป็น parameter ตัวใหม่ แล้ว รัน test ถ้าส่งค่าคงที่เดิมเข้าไป behavior ต้องไม่เปลี่ยน
  4. สำหรับ clone ที่เหลือแต่ละตัว ให้เปลี่ยนเส้นทาง caller มาที่ function ที่ parameterize แล้ว โดยส่ง literal เฉพาะของ clone นั้นเข้าไปเป็น argument
  5. รัน test หลังเปลี่ยนเส้นทางแต่ละ clone เพื่อให้ความล้มเหลวชี้ไปยังการเรียกใช้จุดเดียว
  6. เมื่อไม่เหลือใครเรียก clone เดิมแล้ว ก็ลบทิ้งได้
  7. พิจารณาว่า parameter ใหม่ควรถูกตรวจสอบความถูกต้องหรือไม่ — ตัวอย่างเช่น อัตราส่วนลดที่อยู่นอกช่วงศูนย์ถึงหนึ่งน่าจะเป็นบั๊กที่ควรปฏิเสธ

หยิบท่านี้มาใช้เมื่อ function ต่างกันแค่ค่าคงที่ เมื่อคุณกำลังจะ copy-paste function แล้วปรับตัวเลขตัวเดียว หรือเมื่อชุดของตัวแปรที่มีชื่อต่าง ๆ เพิ่มขึ้นเรื่อย ๆ การ parameterize ฆ่าความซ้ำซ้อนและทำให้มิติที่แปรเปลี่ยนชัดเจนและค้นพบได้

ข้อแลกเปลี่ยนคือ literal ที่โผล่ตรงจุดเรียกอาจอธิบายตัวเองได้น้อยกว่าชื่อ function เดิมที่ตั้งมาดี ๆ เพราะ 0.05 โดด ๆ บอกอะไรน้อยกว่า priceWithFivePercentOff ถ้าชุดค่าที่เป็นไปได้มีไม่กี่ตัวและสื่อความหมาย ก็ใช้ค่าคงที่ที่มีชื่อหรือ enum ตรงจุดเรียกเพื่อดึงความชัดเจนกลับมา และอย่า parameterize เพลินเกินไป เพราะ function ที่มีปุ่มปรับแปดปุ่มใช้ยากกว่า clone ที่มาแทนที่เสียอีก

ใช้ Parameterize Function เมื่อหลีกเลี่ยงเมื่อ
function สองตัวต่างกันแค่ literal เดียวความแตกต่างคือ behavior ไม่ใช่ data
มีตัวแปรใน namespace เพิ่มขึ้นเรื่อย ๆparameter ใหม่ทำให้ signature อ่านยากขึ้น
จะ copy-paste function แล้วเปลี่ยนตัวเลขเดียวfunction ที่ได้มี parameter เกิน 4 ตัว

⚠️ ไม่ควร Parameterize Function เมื่อ:

  • ความแตกต่างระหว่างสอง function คือ behavior — ให้ Remove Flag Argument แทน
  • ค่าที่จะเป็น parameter มีชุดจำกัดและมีความหมาย — ใช้ enum หรือแยก function แทน
  • เพิ่ม parameter เพราะ “อาจจะต้องการ” ในอนาคต — YAGNI
รูปแบบใดส่งสัญญาณว่า Parameterize Function ใช้ได้?
หลังเพิ่ม parameter ให้ function ต้นแบบ อะไรเป็นตัวยืนยันว่า behavior ไม่เปลี่ยน?
ข้อเสียของ argument แบบ literal เปล่า ๆ ที่จุดเรียกใช้คืออะไร?
เมื่อใดที่ความแตกต่างระหว่าง function บ่งชี้ให้แยกออกแทนที่จะ parameterize?