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

การอ่าน code smell

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

flowchart LR
  S["Code smell<br/>(a hint, not a rule)"] --> J{"Worth it<br/>right now?"}
  J -->|yes| R["Pick a matching<br/>refactoring"]
  J -->|no| N["Note it,<br/>move on"]
  R --> T["Small step → test → commit"]
smell คือคำใบ้ — สังเกตให้เห็น ตัดสินว่าคุ้มจะแก้ตอนนี้ไหม แล้วค่อยก้าวทีละนิด

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

Smellสิ่งที่คุณสังเกตเห็นคอร์สนี้แก้ไว้ที่ไหน
Long Functionfunction ที่ยาวเลื่อนพ้นหน้าจอและทำหลายสิ่งComposing MethodsExtract Function
Duplicated Codeชิ้นส่วนเดียวกันปรากฏในสองที่หรือมากกว่าExtract Function และ Moving Features
Mysterious Nameตัวแปรหรือ function ที่ชื่อซ่อนจุดประสงค์ไว้Extract Variable
Large Classclass ที่กักตุน field และความรับผิดชอบไว้มากเกินไปMoving Features
Long Parameter Listการเรียกที่มี argument มากมายจนคุณตามไม่ทันSimplifying APIs
Feature Envymethod ที่สนใจข้อมูลของ object อื่นมากกว่าของตัวเองMoving Features
Primitive Obsessionstring และ number เปล่า ๆ ที่ถูกใช้แทนแนวคิดจริงOrganizing Data
Shotgun Surgeryการเปลี่ยนแปลงเล็ก ๆ หนึ่งครั้งบังคับให้ต้องแก้กระจัดกระจายไปทั่วหลายไฟล์Moving Features
Tangled Conditionalsการแตกแขนงที่ซ้อนลึกหรือซ้ำกันจนซ่อน logic ไว้Simplifying Conditionals
Temporary Field / Inheritance Misuseสถานะที่ถูกใช้เพียงบางครั้ง หรือลำดับชั้นที่ขัดขากับคุณGeneralization & Inheritance

นี่คือ Primitive Obsession คือใช้ number ดิบ ๆ ส่งไปมาเพื่อแทน “เงิน” ทั้งที่ไม่มีอะไรกันไม่ให้ caller เอาไปปนกับจำนวนชิ้นหรืออุณหภูมิ smell ตรงนี้คือการใช้ primitive เปล่า ๆ แทนแนวคิดในโดเมน วิธีรักษาคือห่อไว้ใน type เล็ก ๆ ซึ่งอยู่ใน Organizing Data แต่คุณควรฝึก ดมกลิ่น ให้ออกตั้งแต่ตอนนี้

// Smell: a bare number means "money" — easy to misuse
function total(price: number, shipping: number): number {
return price + shipping;
}
// Hint of the cure: give money its own type
type Money = { cents: number };
function totalMoney(price: Money, shipping: Money): Money {
return { cents: price.cents + shipping.cents };
}

เวอร์ชันที่ถูกห่อไว้ทำให้เป็นไปไม่ได้ที่จะเผลอบวกเงินเข้ากับจำนวน — ตอนนี้ compiler หรือชนิดข้อมูลแบกความหมายที่ number เปล่า ๆ เคยปล่อยให้ขึ้นอยู่กับโชคชะตา

Refactor เมื่อหลีกเลี่ยงเมื่อ
เห็น Code Smell ที่ชัดเจนและตั้งชื่อได้ยังไม่มี test รองรับ
ต้องการ add feature ในพื้นที่นั้นdeadline กำลังจะมาถึงและ feature ยังไม่พร้อม
code ทำให้ review ยากและทีมถามซ้ำ ๆcode นั้นยังไงก็กำลังจะลบทิ้ง

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

  • ยังไม่รู้ว่า code นั้นทำอะไรจริง ๆ — อ่านให้เข้าใจก่อน
  • ไม่มี test และ refactoring จะเปลี่ยน behavior โดยไม่รู้ตัว
  • refactor เพราะความรู้สึก ไม่ใช่เพราะ smell จริง ๆ
code smell คืออะไร?
refactoring ใดที่จัดการกับ Long Function ได้ตรงที่สุด?
smell ใดที่แสดงในตัวอย่าง?
ทำไมจึงควรตั้งชื่อ smell ก่อนเลือก refactoring?