บทที่ 5: Bend, or Break — โค้งงอแทนที่จะหัก
3 min readภาพรวม
ชีวิตไม่เคยหยุดนิ่ง — โค้ดของเราก็เช่นกัน เพื่อให้อยู่รอดในโลกที่เปลี่ยนแปลงเร็ว เราต้องเขียนโค้ดที่ยืดหยุ่น (flexible) มากที่สุด — บทนี้เสนอเทคนิคในการทำให้โค้ด "โค้งงอ" แทนที่จะ "หัก"
Topic 28: Decoupling — การลด coupling
Tip 44: Decoupled Code Is Easier to Change — โค้ดที่ decoupled เปลี่ยนแปลงง่ายกว่า
Tip 45: Tell, Don't Ask — บอก อย่าถาม
Tip 46: Don't Chain Method Calls — อย่าต่อ method calls เป็นลูกโซ่
Tip 47: Avoid Global Data — หลีกเลี่ยง global data
Law of Demeter (LoD): method ของ object ควรเรียกใช้ method ของ:
- ตัวมันเอง
- parameters ที่ส่งเข้ามา
- objects ที่มันสร้างขึ้น
- component objects โดยตรง
ห้าม: เรียก method ของ object ที่ได้มาจากการเรียก method อื่น — แบบ a.b.c.d() — เพราะ chain แบบนี้ทำให้ทุกอย่าง coupled กันหมด
Global Data ร้ายกาจ: global data สร้าง coupling ที่มองไม่เห็นระหว่าง components — ทุกส่วนของโค้ดสามารถอ่าน/เขียนได้ ทำให้ตามหาจุดที่เปลี่ยนค่าได้ยาก และ testing แทบเป็นไปไม่ได้ ถ้าจำเป็นต้องใช้ — wrap ด้วย API
Topic 29: Juggling the Real World — การจัดการโลกความจริง
Tip 48: Events Are Messages — Events คือข้อความ
โลกเป็น asynchronous — เราไม่สามารถบังคับให้ทุกอย่างเกิดตามลำดับได้ สี่กลยุทธ์ในการจัดการ events:
- Finite State Machines: ระบบอยู่ใน state เดียวตลอดเวลา — events ทำให้เกิดการเปลี่ยน state
- The Observer Pattern: source of events (observable) ส่ง events ไปยัง list ของ listeners (observers) ที่ลงทะเบียนไว้ — ข้อเสียคือ coupling เพราะ observable ต้องรู้จัก observers
- Publish/Subscribe (Pub/Sub): ลด coupling — publishers และ subscribers ไม่รู้จักกัน — ทุกคนเชื่อมต่อผ่าน channel — ข้อเสียคือตามหาว่าเกิดอะไรขึ้นในระบบยาก
- Reactive Programming และ Streams: จัดการ events แบบ declarative — values เปลี่ยนแปลงตามเวลา — เหมาะกับ UI, external data sources, และ distributed systems
Topic 30: Transforming Programming — การเขียนโปรแกรมแบบแปลงสภาพ
Tip 49: Programming Is About Code, But Programs Are About Data — โค้ดคือเครื่องมือ แต่โปรแกรมคือข้อมูล
Tip 50: Don't Hoard State; Pass It Around — อย่ากักตุน state — ส่งมันต่อไป
คิดถึงโปรแกรมเป็นการไหลของข้อมูลผ่านการแปลงสภาพ (transformations) — แทนที่จะมี objects คุยกันไปมาเปลี่ยน state กันและกัน (ซึ่งสร้าง coupling) ให้นึกถึง:
data → transform → data → transform → data → ...
Pipeline operator (|>): ทำให้ chain ของ transformation อ่านง่ายราวกับเป็นภาษามนุษย์ — แต่ละ |> คือจุดที่ข้อมูลไหลจาก transformation หนึ่งไปสู่อีกอัน
Error handling ใน pipelines: ใช้ wrapper type (เช่น :ok/:error tuple ใน Elixir หรือ Maybe/Option ใน Haskell/F#) — เมื่อเกิด error, pipeline จะ short-circuit และส่ง error ต่อไปโดยไม่ execute ขั้นตอนที่เหลือ
การคิดแบบ transformational ลด coupling ได้มาก — function สามารถใช้ซ้ำได้ทุกที่ที่ parameters ของมันตรงกับ output ของ function ก่อนหน้า
Topic 31: Inheritance Tax — ภาษีมรดก
Tip 51: Don't Pay Inheritance Tax — อย่าจ่ายภาษีมรดก
Tip 52: Prefer Interfaces to Express Polymorphism — ใช้ interfaces แทน inheritance เพื่อ polymorphism
Tip 53: Delegate to Services: Has-A Trumps Is-A — ใช้ delegation แทน inheritance
Tip 54: Use Mixins to Share Functionality — ใช้ mixins เพื่อแชร์ฟังก์ชัน
Inheritance คือ coupling — เมื่อ parent class เปลี่ยน (แม้แต่ชื่อ instance variable) — child class ก็พัง และโค้ดที่ใช้ child class ก็พังตาม
สามทางเลือกแทน inheritance:
| ทางเลือก | ใช้เมื่อ |
|---|---|
| Interfaces/Protocols | ต้องการระบุว่า class นี้ทำพฤติกรรมอะไรได้บ้าง (polymorphism แบบไม่มี inheritance) |
| Delegation | ต้องการใช้ความสามารถของอีก class โดยไม่ expose API ทั้งหมด — Has-A แทน Is-A |
| Mixins/Traits | ต้องการแชร์ฟังก์ชันระหว่าง classes โดยไม่ต้องมี parent ร่วมกัน |
"You wanted a banana but what you got was a gorilla holding the banana and the entire jungle." — Joe Armstrong
Topic 32: Configuration — การตั้งค่า
Tip 55: Parameterize Your App Using External Configuration
ทำให้แอปพลิเคชันปรับตัวเข้ากับสภาพแวดล้อมที่มันทำงาน — โดยเก็บค่าที่อาจเปลี่ยนแปลงไว้นอกโค้ด
สิ่งที่ควรเก็บใน configuration:
- Credentials สำหรับ external services
- Logging levels และ destinations
- Port, IP address, machine names
- Environment-specific validation parameters
- Tax rates และ license keys
- Site-specific formatting
ควร wrap configuration ด้วย API — ไม่ใช่ global variable — เพื่อ decouple โค้ดจากรายละเอียดของการเก็บ configuration
Configuration-as-a-Service: ใช้ service API สำหรับ configuration — หลายแอปใช้ร่วมกัน, เปลี่ยนได้ globally, เปลี่ยนได้แบบ dynamic โดยไม่ต้อง restart
ข้อควรระวัง: อย่าทำทุกอย่างเป็น configurable — เคยมีบริษัทที่มี 40,000 configuration variables และกลายเป็น nightmare ในการ maintain