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

Substitute Algorithm

function บางตัวอ่านยากไม่ใช่เพราะยาว แต่เพราะ วิธีการ ยุ่งเหยิงเกินจำเป็น เช่น เขียน loop เองทั้งที่ lookup ก็จบ หรือต่อเงื่อนไขเป็นสายทั้งที่ standard library มีให้อยู่แล้ว Substitute Algorithm สลับ body ทั้งก้อนเป็นแบบที่เรียบง่ายกว่าและให้ผลลัพธ์เหมือนกัน ต่างจากท่า composing ตัวเล็ก ๆ ตรงที่ท่านี้เปลี่ยน กลยุทธ์ ไม่ใช่แค่ถ้อยคำ จึงต้องพึ่ง test หนักมากเพื่อพิสูจน์ว่าทั้งสองวิธีให้ผลตรงกัน

function ทำงานได้อยู่ แต่พออ่านแล้วคุณต้องรันในหัวทีละขั้นเพื่อให้มั่นใจว่าถูก logic หนักกว่าที่ปัญหาควรจะเป็น เช่น ไล่ค้นใน list ด้วยมือทั้งที่เช็คสมาชิกใน set ก็ตอบได้ หรือต่อ if เป็นทอด ๆ ทั้งที่สรุปได้ว่า “ค่านี้อยู่ในไม่กี่ค่านี้หรือเปล่า” เมื่อคุณอธิบายงานของ function ได้ด้วยประโยคเดียว แต่ code กินไปทั้งย่อหน้า แปลว่ามี algorithm ที่ชัดกว่ารออยู่แน่นอน

function ตัดสินใจว่ารหัสประเทศได้รับการรองรับหรือไม่ด้วยการวน loop และพลิก flag ตัวแทนใช้การตรวจสอบสมาชิกโดยตรงกับ set ที่กำหนดไว้ตายตัว — คำตอบเดียวกัน อ่านน้อยกว่ามาก

// Before
function isSupported(code: string): boolean {
const supported = ['US', 'CA', 'GB', 'DE'];
let found = false;
for (const c of supported) {
if (c === code) {
found = true;
}
}
return found;
}
// After
const SUPPORTED = new Set(['US', 'CA', 'GB', 'DE']);
function isSupported(code: string): boolean {
return SUPPORTED.has(code);
}
  1. ทำให้แน่ใจว่า function ที่คุณกำลังจะแทนที่ถูกครอบคลุมด้วย test อย่างเต็มที่ รวมถึง edge case — input ว่าง, ไม่พบ match, ค่าที่ขอบเขต ถ้าความครอบคลุมยังบาง ให้เพิ่ม test ก่อน และยืนยันว่าผ่านเมื่อทดสอบกับ code ที่มีอยู่
  2. คิด algorithm ตัวใหม่ให้ครบแล้วเขียนออกมา ทางที่ดีคือเขียนเป็น function แยกวางไว้ข้างตัวเดิม
  3. เปรียบเทียบ behavior ทั้งสอง สำหรับ function ที่มี input space เล็กและนับได้ ให้รันทั้งสองอันกับทุก input แล้ว assert ว่าให้ผลตรงกัน มิฉะนั้นพึ่งพาชุด test บวกกับ case เฉพาะเจาะจงไม่กี่กรณี
  4. สลับ body เก่าเป็นอันใหม่
  5. รัน test ทุก case ต้องยังคงผ่าน
  6. ลบ algorithm เก่าและ helper ที่มีไว้รองรับทิ้ง เมื่อไม่เหลืออะไรอ้างถึงแล้ว

Substitute Algorithm คุ้มค่าเมื่อมีวิธีที่ชัดเจนกว่าหรือเป็นมาตรฐานที่ให้ผลลัพธ์เดียวกัน — แทนที่ loop ที่ทำเองด้วยการเรียก library หรือเงื่อนไขที่แผ่ขยายด้วยการ lookup จากตาราง เวอร์ชันใหม่อ่านง่ายกว่าและมักจะขยายต่อได้ง่ายกว่า

ความเสี่ยงสูงกว่าการเคลื่อนไหวที่เล็กกว่าเพราะคุณกำลังเปลี่ยนกลยุทธ์ทั้งหมด ดังนั้น behavior ที่แยบยลอาจแตกต่างกัน — ลำดับการตัดสินเสมอที่ต่างกัน การจัดการค่าซ้ำหรือ input ว่างที่เปลี่ยนไป จงลองทำเฉพาะเมื่อมี test ที่หนักแน่น และเลือกที่จะแยก function ยักษ์ที่ซับซ้อนยุ่งเหยิงออกเป็นชิ้นเล็ก ๆ ก่อน ที่จะแทนที่ เพื่อให้การแทนที่แต่ละครั้งเล็กและตรวจสอบได้

ใช้ Substitute Algorithm เมื่อหลีกเลี่ยงเมื่อ
algorithm ใหม่ชัดเจน ทดสอบได้ และมี test รองรับalgorithm เดิมซับซ้อนและยังไม่มี test ครอบคลุม
library มี built-in ที่ทำสิ่งเดียวกันได้ดีกว่ากำลังเปลี่ยนเพราะ “ดูดีกว่า” ไม่ใช่เพราะแก้ปัญหาจริง
algorithm เดิม bug-prone และเข้าใจยากalgorithm ใหม่ยังไม่ถูก benchmark ว่า performance เทียบเท่า

⚠️ ไม่ควร Substitute Algorithm เมื่อ:

  • ยังไม่มี test ที่ครอบคลุม behavior เดิมทั้งหมด
  • algorithm ใหม่ยังไม่ได้รับการพิสูจน์ว่าถูกต้องใน edge case ทั้งหมด
  • กำลังเปลี่ยนเพื่อ micro-optimization โดยไม่มี profiling ยืนยัน
Substitute Algorithm แทนที่อะไร?
ทำไม refactoring นี้จึงพึ่งพา test อย่างหนักเป็นพิเศษ?
อะไรคือขั้นเตรียมการที่ดีก่อนแทนที่อัลกอริทึม?
คุณควรจัดการกับ function ยักษ์ที่ซับซ้อนยุ่งเหยิงซึ่งคุณต้องการทำให้เรียบง่ายอย่างไร?