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

การจัดระเบียบข้อมูล

กลุ่ม organizing data ว่าด้วย รูปร่าง ของค่าที่โปรแกรมขนไปมา และ เส้นทาง ที่ code ใช้เข้าถึงค่าเหล่านั้น โปรแกรมจะชัดเจนได้มากที่สุดเท่าที่ข้อมูลชัดเจนเท่านั้น เมื่อ field หนึ่งถูกแตะได้จากทุกที่ เมื่อเลข 0.07 ดิบ ๆ ลอยผ่านสามโมดูลโดยไม่มีชื่อ หรือเมื่อ string ธรรมดาแอบแบกกฎการจัดรูปแบบของตัวเองไว้ ข้อมูลนั้นกำลังทำงานต่อต้านคุณ refactoring กลุ่มนี้ขีดขอบเขตล้อมข้อมูล ตั้งชื่อให้ และปล่อยให้ข้อมูลโตขึ้นพร้อม behavior ที่เรียกร้องมาตลอดอย่างเงียบ ๆ

ข้อมูลที่ยุ่งเหยิงโผล่มาในรูปแบบเล็ก ๆ ที่เกิดซ้ำ ๆ ค่าคงที่ตัวเดียวกันปรากฏในห้าไฟล์โดยไม่มีใครจำได้ว่าแปลว่าอะไร field ที่เป็น list ถูกส่งต่อออกไป แล้ว caller ไกล ๆ ก็ push ลงไป ทำลาย invariant ที่เจ้าของไม่เคยตกลงด้วย เบอร์โทรถูกเก็บเป็น string ทุกการ validate และการจัดรูปแบบจึงถูก copy-paste ซ้ำที่จุดใช้งานทุกแห่ง แต่ละอย่างไม่ร้ายแรงเลย แต่พอรวมกันก็ทำให้ codebase เปราะ เพราะเปลี่ยน วิธีเก็บ ข้อมูลทีเดียว แรงกระเพื่อมจะไปถึงทุกจุดที่อ่านข้อมูลนั้นโดยตรง

Code Smellอาการที่บ่งชี้ท่าที่แนะนำ
Primitive Obsessionprimitive ที่สะสม validation และ formatting ไว้รอบตัวReplace Primitive with Object
Magic Number / Stringค่าคงที่ลอยตัวที่ไม่มีชื่อ โผล่ซ้ำในหลายไฟล์Replace Magic Literal
Mutable Datafield ลิสต์ถูก mutate จากข้างนอก ทำให้ invariant พังEncapsulate Collection
Switch Statementtype code ขับเคลื่อน switch ที่กระจายอยู่ในหลาย functionReplace Type Code with Subclasses
Global Dataตัวแปรที่ถูกอ่านและเขียนจากทุกที่โดยไม่มีจุดควบคุมEncapsulate Variable
flowchart TD
  A["Data that is awkward to work with"] --> B{"What is wrong?"}
  B -->|"A field is read and written everywhere"| C["Encapsulate Variable"]
  B -->|"A bare number or string has no name"| D["Replace Magic Literal"]
  B -->|"A list or map field is mutated from outside"| E["Encapsulate Collection"]
  B -->|"A primitive is sprouting behaviour"| F["Replace Primitive with Object"]
  B -->|"A type code drives branching logic"| G["Replace Type Code with Subclasses"]
  C --> H["Data with a clear, guarded shape"]
  D --> H
  E --> H
  F --> H
  G --> H
การเลือกท่า organizing-data ตามว่าอะไรกำลังผิดพลาด

refactoring สิบท่า แต่ละท่ากระชับความสัมพันธ์ระหว่างข้อมูลกับ code ที่ใช้ข้อมูลนั้น

  • Encapsulate Variable — ห่อหุ้มการเข้าถึงข้อมูลโดยตรงไว้หลัง function accessor เพื่อให้คุณมีจุดเดียวสำหรับตรวจสอบความถูกต้อง บันทึก log หรือเปลี่ยนวิธีแทนค่าในภายหลัง
  • Replace Magic Literal — ให้ชื่อเชิงสัญลักษณ์แก่ตัวเลขหรือ string ที่ไม่มีคำอธิบาย เพื่อให้ค่าคงที่อธิบายความหมายของตัวเอง และเปลี่ยนแปลงได้ในจุดเดียว
  • Encapsulate Collection — หยุดเปิดเผย field ลิสต์หรือแมปที่เปลี่ยนแปลงได้ คืนค่าเป็น view แบบอ่านอย่างเดียว และเสนอ method add กับ remove อย่างชัดเจน เพื่อให้เจ้าของยังควบคุมเนื้อหาได้
  • Replace Primitive with Object — เมื่อค่า primitive เริ่มดูดกฎ validate และกฎจัดรูปแบบเข้ามา ให้ยกระดับเป็น value type เล็ก ๆ ที่ถือทั้งข้อมูลและ behavior
  • Replace Type Code with Subclasses — เมื่อ field Type Code ขับเคลื่อนการแตกกิ่งแบบ switch ให้แทนที่ logic เงื่อนไขด้วย polymorphism ที่กระจายอยู่ทั่วลำดับชั้น class ขนาดเล็ก
  • Self-Encapsulate Field — ให้แม้แต่การอ่านและเขียน field ภายใน class ตัวเองก็วิ่งผ่าน getter และ setter ของ field นั้น subclass และการ validate จะได้มีจุดเดียวให้เกาะ
  • Split Variable — เมื่อตัวแปรหนึ่งถูกกำหนดค่าใหม่เพื่อหมายถึงสองสิ่งที่ต่างกัน ให้แต่ละจุดประสงค์มีตัวแปรที่กำหนดค่าครั้งเดียวเป็นของตัวเอง
  • Rename Field — เมื่อชื่อ field ของ record หรือ struct ไม่ตรงกับข้อมูลอีกต่อไป ให้เปลี่ยนชื่อแล้วอัปเดตทุกจุดที่ใช้ ชื่อจะได้กลับมาบอกความจริง
  • Change Reference to Value (and back) — ทำให้ object ขนาดเล็กเป็น value ที่ไม่เปลี่ยนแปลงและเปรียบเทียบกันด้วยเนื้อหา เมื่อการแบ่งปันไม่ได้ให้ประโยชน์อะไร หรือกลับท่าตรงข้ามเมื่อหลาย ๆ ที่ต้องแบ่งปัน instance เดียวที่อัปเดตได้
  • Replace Type Code with State/Strategy — เมื่อ Type Code ขับเคลื่อน behavior และเปลี่ยนแปลงได้ขณะ runtime ให้ถือ object State หรือ Strategy ที่สลับเปลี่ยนได้ แทนที่จะใช้ subclass ตายตัว

แต่ละบทเรียนแสดงรูปแบบก่อน-และ-หลังเดียวกันในภาษา TypeScript, Python, Go และ Rust จากนั้นจึงเดินผ่านกลไกที่ปลอดภัยและเป็นลำดับ — โดยรัน test ระหว่างทุกขั้นตอน

การรีแฟคเตอร์แบบ "organizing data" เปลี่ยนแปลงอะไรเป็นหลัก?
การรีแฟคเตอร์ใดที่ให้ชื่อเชิงสัญลักษณ์แก่ตัวเลขหรือ string ที่ไม่มีคำอธิบาย?
แนวคิดที่เกิดซ้ำใดที่ร้อยเรียงโมดูลนี้เข้าด้วยกัน?