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

Inline Class

เอา class ที่หดจนแทบไม่เหลืออะไร คือมี field ไม่กี่ตัวกับ method หนึ่งสองตัว ซึ่งไม่คุ้มจะเป็น type แยกอีกแล้ว มายุบรวมกับ class ที่ถืออยู่ behavior ไม่เปลี่ยนเลย คุณแค่ถอดชั้นที่หมดประโยชน์ออกไป

นี่คือสิ่งตรงข้ามของ Extract Class และเป็นยารักษา class ที่กลายเป็นชั้นกลางที่ไม่จำเป็น สัญญาณบ่งชี้: class ที่มี field เดียวและ method เล็กน้อย type ที่มีอยู่เพียงเพื่อถูกห่อโดย caller เดียว หรือความรับผิดชอบในอดีตที่การ refactor ก่อนหน้าได้ทำให้กลวงเปล่า หากการอ่าน code หมายถึงการกระโดดไปยัง class เล็ก ๆ แล้วกลับมาทันที การกระโดดนั้นเป็นภาระล้วน ๆ

class TrackingInformation ถือ field สองตัวและจัดรูปแบบให้ โดยมี Shipment ใช้อยู่รายเดียว ตอนแรกข้อมูลกับ method เดียวนั้นอยู่ใน type แยก หลัง refactor ทั้งหมดพับเข้าไปอยู่ใน Shipment

// Before
class TrackingInformation {
constructor(public shippingCompany: string, public trackingNumber: string) {}
display(): string {
return `${this.shippingCompany}: ${this.trackingNumber}`;
}
}
class Shipment {
constructor(public trackingInfo: TrackingInformation) {}
status(): string {
return `Shipped via ${this.trackingInfo.display()}`;
}
}
// After
class Shipment {
constructor(public shippingCompany: string, public trackingNumber: string) {}
private trackingDisplay(): string {
return `${this.shippingCompany}: ${this.trackingNumber}`;
}
status(): string {
return `Shipped via ${this.trackingDisplay()}`;
}
}
classDiagram
  class ShipmentBefore {
    trackingInfo
    status()
  }
  class TrackingInformation {
    shippingCompany
    trackingNumber
    display()
  }
  class ShipmentAfter {
    shippingCompany
    trackingNumber
    status()
    trackingDisplay()
  }
  ShipmentBefore --> TrackingInformation : has a
  TrackingInformation ..> ShipmentAfter : Inline Class
TrackingInformation บาง ๆ พับกลับเข้าไปใน Shipment
  1. บน class ที่ดูดซับ ให้ประกาศ method สาธารณะของ class ที่คุณกำลังจะ inline และให้แต่ละตัวเพียงมอบหมาย (delegate) ไปยัง instance ภายในไปก่อน
  2. อัปเดต caller ทุกตัวของ class ที่กำลังจะลบ ให้เรียกผ่าน class ที่ดูดซับแทน
  3. รัน test เพื่อยืนยันว่าการมอบหมายทำงานเหมือนกันทุกประการ
  4. ย้าย field และ method ของ class ภายในเข้าไปใน class ที่ดูดซับทีละตัว โดยใช้ Move Field และ Move Function
  5. แทนที่บอดี้ของ method ที่มอบหมายแต่ละตัวด้วย logic จริงที่ตอนนี้อาศัยอยู่ในเครื่องแล้ว
  6. รัน test หลังการย้ายแต่ละครั้ง เพื่อให้ความล้มเหลวชี้ไปยังขั้นตอนเดียว
  7. ลบ class ที่ตอนนี้ว่างเปล่าทิ้ง เมื่อไม่เหลืออะไรอ้างถึงแล้ว

Inline class เมื่อหดเล็กลงเหลือ field เดียวที่มี behavior เล็กน้อย เมื่อมีอยู่เพียงเพื่อห่อ caller เดียว หรือเมื่อการแยกครั้งก่อนไม่เคยเติบโตเป็นความรับผิดชอบที่แท้จริงและตอนนี้เพียงเพิ่มการอ้อมค้อม

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

ใช้ Inline Class เมื่อหลีกเลี่ยงเมื่อ
class นั้นไม่ได้ทำอะไรมากพอที่จะมีชีวิตอยู่เองclass นั้นมี responsibility ที่ชัดเจน แม้จะเล็ก
หลัง refactoring ครั้งก่อน class กลายเป็นเปลือกclass นั้นถูกใช้โดยหลาย class — inline จะกระจาย dependency
ต้องการรวม logic ก่อนจะแบ่งใหม่ด้วย Extract Classclass มี behavior ที่อาจขยายในอนาคต

⚠️ ไม่ควร Inline Class เมื่อ:

  • class นั้นมี test ของตัวเอง — inline จะทำ test ซับซ้อนขึ้น
  • ชื่อ class นั้นสื่อ intent ที่มีคุณค่า แม้ body จะเล็ก
  • กำลัง inline เพราะ “ดูน้อยลง” ไม่ใช่เพราะลด complexity จริง
เมื่อใดที่ Inline Class เป็นการย้ายที่ถูกต้อง?
Inline Class เป็นสิ่งตรงข้ามของการ refactor ใด?
อะไรคือความเสี่ยงของการ inline class ที่ยังทำงานจริงอยู่?
อะไรคือขั้นตอนแรกที่แนะนำเมื่อ inline class?