การทำให้ API เรียบง่าย
จุดประสงค์
หัวข้อที่มีชื่อว่า “จุดประสงค์”กลุ่ม simplifying APIs ว่าด้วยเรื่อง ภายนอก ของ function นั่นคือส่วนที่ caller ทุกคนต้องอ่านและทำความเข้าใจ function อาจมีเนื้อในที่สมบูรณ์แบบแต่ก็ยังเจ็บปวดเวลาใช้งานได้ เพราะชื่อโกหก รายการ argument ยาวเป็นไมล์ หรือ boolean ที่หลงเข้ามาเปลี่ยนสิ่งที่ทำอย่างเงียบ ๆ การรีแฟกเตอร์เหล่านี้จัดระเบียบจุดเรียกใช้ให้เรียบร้อย เพื่อให้ interface โฆษณาสิ่งที่มอบให้ได้อย่างตรงไปตรงมา
Code Smell
หัวข้อที่มีชื่อว่า “Code Smell”โดยทั่วไปคุณจะรู้สึกได้ถึง interface ที่ไม่ดีตรงจุดเรียกใช้ คุณหรี่ตามองรายการ argument แล้วจำไม่ได้ว่า true ตัวไหนหมายถึงอะไร คุณเจอ function สองตัวที่เหมือนกันเก้าสิบเปอร์เซ็นต์และต่างกันแค่ตัวเลขที่ฝังไว้ตายตัว คุณส่งค่าสามตัวเดิมไปด้วยกันเรื่อย ๆ ในลำดับเดิม ให้กับทุก function ในโมดูล สิ่งเหล่านี้ไม่ใช่บั๊ก เพราะ code ก็ทำงานได้ แต่แต่ละอย่างเก็บภาษีจากทุกคนที่อ่านจุดเรียกใช้
| Code Smell | อาการที่บ่งชี้ | ท่าที่แนะนำ |
|---|---|---|
| Long Parameter List | parameter เกิน 3-4 ตัว หรือชุดเดิมปรากฏในหลาย function | Introduce Parameter Object |
| Flag Argument | boolean ที่ call site — true ตัวนี้หมายถึงอะไรกันแน่? | Remove Flag Argument |
| Duplicated Code | function สองตัวที่ต่างกันแค่ค่า literal เดียว | Parameterize Function |
| Feature Envy | caller แกะหลาย field จาก object เพียงเพื่อส่งต่อ | Preserve Whole Object |
| Misleading Name | ต้องเปิด implementation เพื่อรู้ว่า function ทำอะไร | Rename Function |
flowchart TD
A["A function that is awkward to call"] --> B{"What makes it awkward?"}
B -->|"The name misleads or hides intent"| C["Rename Function/Variable"]
B -->|"Two near-clones differ by a value"| D["Parameterize Function"]
B -->|"Too many arguments travel together"| E["Introduce Parameter Object"]
B -->|"A boolean flips behaviour"| F["Remove Flag Argument"]
B -->|"The constructor is too rigid"| G["Replace Constructor with Factory"]
C --> H["An interface that explains itself"]
D --> H
E --> H
F --> H
G --> H โมดูลนี้ครอบคลุมอะไรบ้าง
หัวข้อที่มีชื่อว่า “โมดูลนี้ครอบคลุมอะไรบ้าง”การรีแฟกเตอร์เก้าแบบที่ทำให้ interface ของ function และ method ชัดเจนและเรียกใช้ง่ายขึ้น:
- Rename Function/Variable — เมื่อชื่อชวนเข้าใจผิดหรืออธิบายได้ไม่พอ ให้เปลี่ยนชื่อแล้วอัปเดต caller ทุกจุด ชื่อคือเอกสารที่ราคาถูกที่สุดที่คุณจะได้เขียน
- Parameterize Function — เมื่อ function สองตัวที่เกือบเหมือนกันต่างกันแค่ค่า literal ให้ยุบเหลือ function เดียวที่รับค่านั้นเป็น parameter
- Introduce Parameter Object — เมื่อ argument กลุ่มหนึ่งเดินทางไปด้วยกันเสมอ ให้รวบเป็น object เดียว ความสัมพันธ์จะได้ชัดเจน
- Remove Flag Argument — เมื่อ argument แบบ boolean สลับ behavior ให้แยก function ออกเป็นสอง function ที่มีชื่อชัดเจน
- Replace Constructor with Factory Function — เมื่อ constructor จำกัดเกินไป ให้ห่อด้วย factory function ที่เลือก subtype ได้และตั้งชื่อสื่อความหมายได้
- Separate Query from Modifier — เมื่อ function ทั้งคืนค่าและเปลี่ยนสถานะ ให้แยกออกเป็น query บริสุทธิ์กับ command คนละตัว
- Preserve Whole Object — เมื่อ caller แกะหลาย field ออกจาก object แค่เพื่อส่งต่อไป ให้ส่งทั้ง object ไปแทน
- Replace Parameter with Query — เมื่อ parameter สามารถหาได้จากข้อมูลที่ function มีอยู่แล้ว ให้ตัดทิ้งแล้วคำนวณข้างในเอง
- Remove Setting Method — เมื่อ field ควรถูกตรึงไว้ตั้งแต่ตอนสร้าง ให้ลบ setter เพื่อทำให้ object เปลี่ยนแปลงไม่ได้
แต่ละบทเรียนแสดงตัวอย่างก่อนและหลังแบบเดียวกันใน TypeScript, Python, Go และ Rust แล้วพาเดินผ่านกลไกที่ปลอดภัยและเรียงลำดับเป็นขั้นตอน — โดยรัน test ระหว่างทุกขั้น