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

การทำให้ API เรียบง่าย

กลุ่ม simplifying APIs ว่าด้วยเรื่อง ภายนอก ของ function นั่นคือส่วนที่ caller ทุกคนต้องอ่านและทำความเข้าใจ function อาจมีเนื้อในที่สมบูรณ์แบบแต่ก็ยังเจ็บปวดเวลาใช้งานได้ เพราะชื่อโกหก รายการ argument ยาวเป็นไมล์ หรือ boolean ที่หลงเข้ามาเปลี่ยนสิ่งที่ทำอย่างเงียบ ๆ การรีแฟกเตอร์เหล่านี้จัดระเบียบจุดเรียกใช้ให้เรียบร้อย เพื่อให้ interface โฆษณาสิ่งที่มอบให้ได้อย่างตรงไปตรงมา

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

Code Smellอาการที่บ่งชี้ท่าที่แนะนำ
Long Parameter Listparameter เกิน 3-4 ตัว หรือชุดเดิมปรากฏในหลาย functionIntroduce Parameter Object
Flag Argumentboolean ที่ call site — true ตัวนี้หมายถึงอะไรกันแน่?Remove Flag Argument
Duplicated Codefunction สองตัวที่ต่างกันแค่ค่า literal เดียวParameterize Function
Feature Envycaller แกะหลาย 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
เลือกท่า simplifying-APIs จากสิ่งที่ทำให้การเรียกใช้ดูเก้กัง

การรีแฟกเตอร์เก้าแบบที่ทำให้ 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 ระหว่างทุกขั้น

การรีแฟกเตอร์กลุ่ม "simplifying APIs" ปรับปรุงส่วนไหนของ function เป็นหลัก?
การรีแฟกเตอร์แบบใดจัดการกับ function สองตัวที่เกือบเหมือนกันซึ่งต่างกันแค่ค่า literal?
ทำไมการปรับปรุง interface จึงมักคุ้มค่ากว่าการปรับปรุงเนื้อใน?