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

Consolidate Conditional Expression

เอาชุดเงื่อนไขแยกกันที่แต่ละตัวคุ้มกันผลลัพธ์ เดียวกัน มาพับรวมเป็นเงื่อนไขผสมเดียว แล้วสกัดเงื่อนไขนั้นออกมาเป็น function ที่มีชื่อ การตรวจสอบสามตัวที่กระจัดกระจายซึ่งมี return 0 เหมือนกัน กลายเป็น if (isNotEligible(employee)) return 0 เพียงตัวเดียว และเหตุผลของผลลัพธ์ก็ถูกจับใจความไว้ในชื่อ

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

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

// Before
function disabilityAmount(employee: Employee): number {
if (employee.seniority < 2) return 0;
if (employee.monthsDisabled > 12) return 0;
if (employee.isPartTime) return 0;
// ... compute the real amount
return baseAmount(employee);
}
// After
function disabilityAmount(employee: Employee): number {
if (isNotEligible(employee)) return 0;
// ... compute the real amount
return baseAmount(employee);
}
function isNotEligible(employee: Employee): boolean {
return (
employee.seniority < 2 ||
employee.monthsDisabled > 12 ||
employee.isPartTime
);
}
flowchart LR
  subgraph Before["Before"]
    A["if seniority < 2 return 0<br/>if monthsDisabled > 12 return 0<br/>if isPartTime return 0"]
  end
  subgraph After["After"]
    B["if isNotEligible(emp)<br/>return 0"]
  end
  Before -.->|"Consolidate Conditional Expression"| After
การตรวจสอบสามตัวที่มีผลลัพธ์เดียวกลายเป็นเงื่อนไขที่มีชื่อตัวเดียว
  1. ยืนยันว่าเงื่อนไขเหล่านั้นมีผลลัพธ์ร่วมกันจริง ๆ และไม่มีผลข้างเคียง (side effect) ระหว่างกัน หากการตรวจสอบหนึ่งเปลี่ยนสถานะที่ตัวถัดไปพึ่งพา อย่ารวม
  2. รวมการทดสอบด้วย || เมื่อทั้งหมดนำไปสู่สาขาเดียวกัน (หรือ && เมื่อโครงสร้างเป็นการตรวจสอบซ้อนกันที่ต้องผ่านทั้งหมด) วางเงื่อนไขที่รวมแล้วไว้ติดกับผลลัพธ์ร่วม
  3. รัน test behavior ต้องตรงกับโซ่ของ if แยกกันทุกประการ รวมถึงลำดับการลัดวงจร (short-circuit)
  4. ใช้ Extract Function กับเงื่อนไขที่รวมแล้ว ตั้งชื่อตามคำถามที่เงื่อนไขนั้นตอบ (isNotEligible)
  5. รัน test อีกครั้ง

หยิบ Consolidate Conditional Expression มาใช้ทุกครั้งที่เห็นการตรวจสอบเรียงติดกันแล้วทุกตัวให้ผลลัพธ์เดียวกัน ท่านี้มีค่าที่สุดในฐานะท่าตั้งต้น เพราะพอการตรวจสอบทั้งชุดกลายเป็น function ชื่อเดียว คุณก็เอากลับมาใช้ซ้ำได้ และมักเปิดทางให้ Replace Nested Conditional with Guard Clauses ต่อทันที

อย่ารวมถ้าการตรวจสอบแต่ละตัวเป็นการตัดสินใจอิสระจริง ๆ ที่บังเอิญให้ผลเหมือนกันวันนี้ แต่พรุ่งนี้อาจแยกทางกัน เพราะรวมแล้วเท่ากับมัดกฎที่ไม่เกี่ยวกันเข้าด้วยกัน และห้ามรวมข้าม side effect เด็ดขาด เพราะการ short-circuit ของ expression ที่รวมแล้วอาจข้ามงานที่ chain เดิมเคยทำ

ใช้ Consolidate Conditional Expression เมื่อหลีกเลี่ยงเมื่อ
หลายเงื่อนไขต่างนำไปสู่ผลลัพธ์เดียวกันเงื่อนไขมี side effect ระหว่างกัน
ต้องการ Extract Function ตั้งชื่อแนวคิดนั้นเงื่อนไขเป็นกฎที่เป็นอิสระที่อาจแยกทางกันทีหลัง
เงื่อนไขที่กระจัดกระจายซ่อนแนวคิดเดียวการรวมทำให้ short-circuit ข้าม side effect สำคัญ

⚠️ ไม่ควร Consolidate Conditional Expression เมื่อ:

  • เงื่อนไขแต่ละตัวมี side effect ที่ต้องรันทุกครั้ง — การรวมด้วย || จะ short-circuit ข้ามได้
  • ผลลัพธ์เหมือนกันในวันนี้แต่เหตุผลต่างกัน — อาจแยกทางในอนาคต
  • การรวมทำให้เงื่อนไขยาวเกินกว่าจะตั้งชื่อที่ชัดเจนได้
เงื่อนไขก่อนหน้า (precondition) สำหรับการรวมลำดับของเงื่อนไขคืออะไร?
หลังจากรวมเงื่อนไขแล้ว ขั้นตอนถัดไปที่แนะนำคืออะไร?
เมื่อไรที่คุณไม่ควรรวมการตรวจสอบสองตัว?
ทำไมคุณต้องระวังผลข้างเคียงระหว่างการตรวจสอบที่รวมเข้าด้วยกัน?