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

Split Variable

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

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

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

// Before
function describe(height: number, width: number): string {
let temp = 2 * (height + width);
const out: string[] = [`Perimeter: ${temp}`];
temp = height * width;
out.push(`Area: ${temp}`);
return out.join('\n');
}
// After
function describe(height: number, width: number): string {
const perimeter = 2 * (height + width);
const area = height * width;
return [`Perimeter: ${perimeter}`, `Area: ${area}`].join('\n');
}
  1. หาการกำหนดค่าครั้งแรก แล้วเปลี่ยนชื่อตัวแปรให้อธิบาย เฉพาะความหมายแรก นั้น ถ้าภาษาอนุญาต ให้ทำเป็นค่าคงที่ไปเลย (const, let ที่ไม่ reassign หรือ := ที่ไม่เคยกำหนดค่าใหม่)
  2. เปลี่ยนทุกการอ้างอิงระหว่างการกำหนดค่าครั้งแรกและครั้งที่สองให้ใช้ชื่อใหม่
  3. รัน test
  4. ทำซ้ำที่การกำหนดค่าครั้งที่สอง: ประกาศตัวแปรใหม่ด้วยชื่อสำหรับความหมายที่สอง แล้วชี้การอ้างอิงหลังจากนั้นไปยังชื่อใหม่
  5. ทำต่อไปจนกระทั่งแต่ละจุดประสงค์ที่แตกต่างเป็นเจ้าของตัวแปรที่แยกกัน กำหนดค่าครั้งเดียว
  6. เมื่อตัวแปรที่แก้ค่าได้ตัวเดิมไม่เหลือคนอ่านแล้ว ให้ลบทิ้ง ส่วนใน Rust มักตัด mut ออกได้หมดเลย

ใช้ Split Variable ทุกครั้งที่ชื่อหนึ่งถูกกำหนดค่าใหม่เพื่อแทนของคนละอย่าง เพราะจะกำจัดบั๊กทั้งตระกูล “ตอนนี้ค่านี้คืออะไร” ออกไป และทำให้แต่ละบรรทัดเข้าใจได้ด้วยตัวเอง ท่านี้ยังเป็นจุดตั้งต้นของ Extract Function ที่พบบ่อย เพราะพอแต่ละค่าอยู่ในตัวแปรที่กำหนดค่าครั้งเดียวของตัวเอง ส่วนที่คำนวณค่านั้นก็ยกออกมาได้สะอาด

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

ใช้ Split Variable เมื่อหลีกเลี่ยงเมื่อ
variable ถูก reassign เพื่อใช้งานที่ต่างกันvariable เป็น loop counter ที่ชัดเจน (i, j)
ชื่อ variable ไม่สามารถสื่อ intent ได้ชัดเจนทั้งสองค่าเกี่ยวข้องกันแน่นและ split แล้วสับสนกว่า
tracking bug พบว่า variable มีหลายความหมายsplit จะเพิ่ม variable ที่ used-once และทิ้งทันที

⚠️ ไม่ควร Split Variable เมื่อ:

  • loop variable ที่ทุกคนรู้ว่า i หมายถึงอะไร
  • accumulator ที่ชัดเจนว่าสะสมค่าเดียวกันตลอด
  • split แล้วต้องส่งผ่านหลาย scope ทำให้ซับซ้อนขึ้น
เมื่อใดที่คุณควรแยกตัวแปรออกเป็นสองตัว?
การกำหนดค่าซ้ำชนิดใดที่คุณไม่ควรแยก?
หลังจากแยกแล้ว แต่ละตัวแปรที่ได้ควรมี property ใด?
Split Variable มักเป็นจุดเริ่มต้นของการรีแฟคเตอร์ใด?