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

Composing Methods

กลุ่ม composing methods ว่าด้วยเรื่อง ภายใน ของ function ล้วน ๆ คือ statement ถูกจัดกลุ่ม ตั้งชื่อ และเรียงลำดับกันอย่างไร refactoring กลุ่มนี้ไม่แตะ behavior แต่เปลี่ยนว่าคนอ่านจะตามสิ่งที่ function ต้องการสื่อได้ง่ายแค่ไหน

function โตขึ้นตามเวลา สะสม temporary variable ไว้ตรงนี้ เงื่อนไขบรรทัดเดียวสุดฉลาดตรงนั้น แล้วก็ loop ที่แอบทำงานสามอย่างพร้อมกันเงียบ ๆ สุดท้ายก็ไม่มีใครอ่านจากบนลงล่างแล้วสรุปได้ในประโยคเดียว วิธีรักษาในโมดูลนี้จัดการปัญหานั้นตรง ๆ คือแยกชิ้นส่วนที่พันกันออกจากกัน ตั้งชื่อให้ส่วนสำคัญ แล้วปล่อยให้ code ที่เหลืออ่านได้เหมือนสรุปเจตนาสั้น ๆ

Code Smellอาการที่บ่งชี้ท่าที่แนะนำ
Long Methodfunction ยาวจนอ่านจากบนลงล่างแล้วอธิบายไม่ได้ในประโยคเดียวExtract Function
Duplicated Codelogic เดิมซ้ำในหลายจุด คัดลอก-วางมาพร้อมบั๊กExtract Function
Cryptic Expressionsub-expression ที่อ่านแล้วไม่รู้ความหมายExtract Variable
Temporary Variabletemporary variable ที่คำนวณค่าซับซ้อนแล้วส่งต่อReplace Temp with Query
Divergent Changefunction เดียวทำสองงานที่ต่างกัน แก้ที่หนึ่งกระทบอีกที่Split Phase
flowchart TD
  A["A long, tangled function"] --> B{"What is wrong?"}
  B -->|"A block does one named thing"| C["Extract Function"]
  B -->|"A helper adds no clarity"| D["Inline Function"]
  B -->|"A sub-expression is cryptic"| E["Extract Variable"]
  B -->|"A temp holds a computed value"| F["Replace Temp with Query"]
  B -->|"Two unrelated jobs in one flow"| G["Split Phase"]
  C --> H["Code that reads like intent"]
  D --> H
  E --> H
  F --> H
  G --> H
เลือกท่า composing-methods จากอาการที่เจอ

refactoring เก้าแบบ แต่ละแบบเล็กและย้อนกลับได้:

  • Extract Function — ยกชิ้นส่วนหนึ่งขึ้นไปเป็น function ที่มีชื่อของตัวเอง เพื่อให้จุดที่เรียกอ่านได้เหมือนรายการของเจตนา เป็นท่าพื้นฐานที่สุดของทั้งหมด
  • Inline Function — ท่าตรงข้าม พอ body ของ helper ง่ายพอ ๆ กับชื่อ ก็พับกลับเข้าไป
  • Extract Variable — ตั้งชื่อให้ sub-expression ที่ชวนสับสน เพื่อให้บรรทัดนั้นอธิบายตัวเองได้
  • Inline Variable — ท่าตรงข้าม ลบตัวแปรที่เป็นแค่เสียงสะท้อนของ expression และไม่ได้เพิ่มความชัดเจนอะไร
  • Replace Temp with Query — เปลี่ยนตัวแปร local ที่เก็บค่าคำนวณไว้ให้กลายเป็น function เพื่อให้เอาการคำนวณกลับมาใช้ซ้ำและ extract ต่อได้ง่าย
  • Slide Statements — ย้าย statement ที่เกี่ยวข้องกันมาอยู่ติดกัน ชิ้นส่วนที่เข้าชุดกันจะได้พร้อมให้อ่านหรือ extract
  • Split Phase — พอบล็อกเดียวทำงานสองอย่างที่ไม่เกี่ยวกัน ก็หั่นออกเป็น phase ที่เรียงลำดับ แล้วเชื่อมด้วยค่ากลางที่ชัดเจน
  • Substitute Algorithm — เปลี่ยนวิธีทำที่ซับซ้อนยุ่งเหยิงเป็นวิธีที่ชัดเจนกว่าและให้ผลลัพธ์เดียวกัน
  • Replace Loop with Pipeline — แปลง loop แบบ imperative ให้เป็นโซ่ของขั้นตอนที่มีชื่อ ได้แก่ filter, map, reduce

แต่ละบทเรียนใช้ตัวอย่าง before-and-after ชุดเดียวกันใน TypeScript, Python, Go และ Rust แล้วพาไล่ mechanics ทีละขั้นแบบปลอดภัย โดยรัน test คั่นทุกขั้นตอน

refactoring แบบ "composing methods" เปลี่ยนอะไรเป็นหลัก?
refactoring แบบใดที่บทเรียนเรียกว่าเป็นท่าพื้นฐานที่สุดในโมดูลนี้?
ทำไมชุด test จึงสำคัญเวลาใช้ refactoring เหล่านี้?