Moving Features
จุดประสงค์
หัวข้อที่มีชื่อว่า “จุดประสงค์”กลุ่ม moving features เป็นเรื่องของ ตำแหน่ง การ refactor แบบ composing-methods จะปรับรูปร่างภายในของ function แต่การย้ายเหล่านี้จะตัดสินว่า function field หรือกลุ่ม behavior ทั้งก้อนควรไปอยู่ใน class หรือโมดูลใด โปรแกรมยังคงทำสิ่งเดิมหลังจากนั้น — แต่งานแต่ละชิ้นจะอยู่ติดกับข้อมูลและผู้ร่วมงานที่ใช้จริง
อาการของปัญหา
หัวข้อที่มีชื่อว่า “อาการของปัญหา”การตัดสินใจเรื่องซอฟต์แวร์ในวันแรกแทบไม่เคยรอดมาถึงวันที่ร้อย function ที่เคยเป็นของ Order ค่อย ๆ เติบโตจนอ่านข้อมูลส่วนใหญ่จาก Customer field ที่เพิ่มเข้าไปใน record หนึ่งกลับถูกแตะต้องทุกที่ยกเว้น record นั้น class เล็ก ๆ ค่อย ๆ ดูดซับความรับผิดชอบที่สองเข้ามาอย่างเงียบ ๆ จนกลายเป็นสอง class ที่สวมชื่อเดียวกัน เมื่อ behavior กับข้อมูลที่ต้องใช้อยู่ห่างกัน ทุกการแก้ก็แปลว่าต้องกระโดดไปมาระหว่างไฟล์ ท่าย้ายในโมดูลนี้จะดึงทั้งสองอย่างกลับมาอยู่ด้วยกัน
| Code Smell | อาการที่บ่งชี้ | ท่าที่แนะนำ |
|---|---|---|
| Feature Envy | function อ้างอิงข้อมูลของ class อื่นมากกว่า class ตัวเอง | Move Function |
| Data Clumps | field ชุดเดิมเดินทางไปด้วยกันในหลาย function | Move Field |
| Large Class | class ทำสองงานที่ไม่เกี่ยวกัน เปลี่ยนด้านหนึ่งกระทบอีกด้าน | Extract Class |
| Inappropriate Intimacy | caller เดินตามสาย object เพื่อถึง dependency | Hide Delegate |
| Middle Man | class ส่งต่อทุก method call โดยไม่เพิ่มค่า | Inline Class |
flowchart TD
A["A feature sits in the wrong place"] --> B{"What is misplaced?"}
B -->|"A function leans on another context"| C["Move Function"]
B -->|"A field is used elsewhere more"| D["Move Field"]
B -->|"One class does two jobs"| E["Extract Class"]
B -->|"A class no longer earns its keep"| F["Inline Class"]
B -->|"Callers navigate an object chain"| G["Hide Delegate"]
C --> H["Each module owns the right work"]
D --> H
E --> H
F --> H
G --> H โมดูลนี้ครอบคลุมอะไรบ้าง
หัวข้อที่มีชื่อว่า “โมดูลนี้ครอบคลุมอะไรบ้าง”การ refactor เก้าแบบ ซึ่งแต่ละแบบย้อนกลับได้:
- Move Function — เมื่อ function อ้างอิงข้อมูลและ method ของ object อื่นมากกว่าของตัวเอง (Feature Envy) ให้ย้ายไปอยู่ class ที่ function ตัวนั้นอิจฉา
- Move Field — เมื่อ field ถูกอ่านและเขียนจาก record อื่นมากกว่าบ้านปัจจุบัน ให้ย้ายไปอยู่ที่ที่ใช้งานจริง
- Extract Class — เมื่อ class หนึ่งเติบโตจนทำสองงานที่ต่างกัน (Large Class) ให้แยกความรับผิดชอบหนึ่งออกไปเป็น class ใหม่
- Inline Class — ท่ากลับกัน เมื่อ class ไม่คุ้มจะมีอยู่ต่อไป ให้พับกลับเข้า class ที่ใช้งาน
- Hide Delegate (และท่ากลับกันคือ Remove Middle Man) — ห่อหุ้มการมอบหมาย (delegation) เพื่อให้ caller เลิกเดินไล่ตามสายของ object ตามหลัก Law of Demeter
- Move Statements into Function — เมื่อ statement เดิมรันติดกับการเรียก function ทุกครั้ง ให้พับเข้าไปใน function เสีย caller จะได้ลืมไม่ได้
- Move Statements to Callers — สิ่งตรงข้าม: เมื่อ function ทำงานเกินไปหนึ่งอย่างสำหรับ caller บางราย ให้ดันคำสั่งที่แตกต่างกันออกไปยัง caller เหล่านั้น
- Split Loop — เมื่อ loop เดียวทำสองงานที่ไม่เกี่ยวกัน ให้ซอยเป็นสอง loop ที่ทำงานเดียว เพื่อความชัดเจน
- Combine Functions into Class — เมื่อ function คอยส่งข้อมูลก้อนเดิมไปมา ให้รวบเข้าเป็น class ที่เก็บข้อมูลนั้นไว้เป็น field
แต่ละบทเรียนแสดง before-and-after เดียวกันใน TypeScript, Python, Go และ Rust จากนั้นพาเดินผ่านกลไก (mechanics) ที่ปลอดภัยและเป็นลำดับขั้น — โดยรัน test ระหว่างทุกขั้นตอน