จากบทความในโค้ดทัวร์ ตอน 1 ทำการอธิบาย code ที่เขียนด้วยภาษา Go
มีรูปแบบหรือ pattern ที่น่าสนใจหลายตัว
หนึ่งในนั้นคือ NewEntry(url string)

ทำให้นึกถึงเรื่องของ A Theory of modern Go
ซึ่งอธิบายถึงเกี่ยวกับ Global state/variable
จึงนำมาสรุปไว้นิดหน่อย

ในภาษา Go นั้นพยายามจะไม่มี magic ในตัวภาษา

เนื่องจาก magic เป็นสิ่งที่ไม่ดี ส่งผลเสียเยอะ แต่ก็มีข้อดีนะ
เพื่อทำให้ code อ่านเข้าใจได้ง่าย
เพื่อทำให้ code ดูแลรักษาได้ง่าย

แต่โชคไม่ดีเท่าไร
ที่ Go developer มักจะสร้าง magic ขึ้นมาใช้เอง
ยกตัวอย่างเช่น Global state/variable หรือ variable ในระดับ package !!
ซึ่งทำให้ code อ่านและดูแลรักษายากมาก ๆ

คำถาม ใครทำแบบนี้อยู่บ้าง ?

จาก tweet ของ Dave Cheney พูดถึง Modern Go ไว้ว่า

  • No side effect imports
  • No package level variables

นั่นคือ ลด ละ เลิก Global state/variable ในระดับ package !!
หรือการใช้งานให้น้อยที่สุด

มาดูแนวทางกันที่อธิบายไว้ในบทความ

สร้าง function NewXxxx() ขึ้นมาใช้งาน
ใช้สำหรับการสร้าง instance ซึ่งทำหน้าที่เหมือนกับ constructor นั่นเอง
ดังนั้นผู้เรียกใช้ก็ทำการสร้าง instance ผ่านทาง function นี้
ที่สำคัญต้องสร้างข้อตกลงร่วมกันภายในทีมด้วย
ว่าต้องไม่สร้าง instance แบบตรง ๆ เหมือนเดิม

ยังไม่พอนะ ถ้าใน NewXxx() มี dependency ต่าง ๆ ด้วย

คำอธิบาย
จะเห็นได้ว่ามีการ function จาก package
database/sql.Conn, logger และ pool
เป็นการทำงานภายในของ function นี้
เรียกว่าคือ dependency ซึ่งทำให้เกิดผลกระทบตามมา

ทั้ง dbconn, logger และ pool ต่างก็เป็น variable ในระดับ package !!
นั่นหมายความว่า
ผู้เรียกใช้งานและเปลี่ยนค่าต่าง ๆ ได้เลย
ทำให้ไม่สามารถคาดการได้เลยว่าจะเกิดอะไรขึ้นบ้าง ?
database จะล่มไหม ?
network จะช้าไหม ?
ดึงข้อมูลจาก database ถูกหรือไม่ ?
จะมี request ใน pool ไหม ?

ดังนั้นสิ่งที่ควรทำคือ ทำการส่งค่า dependency ต่าง ๆ ผ่าน parameter เข้ามาได้เลย

เรามักเรียกวิธีการนี้ว่า Dependency Injection นั่นเอง
ทำให้ผู้ใช้งานอ่านเข้าใจได้ทันทีว่า
ถ้าต้องการให้ทำงานอย่างถูกต้องแล้ว
ต้องทำการส่ง parameter อะไรเข้ามาบ้าง
เขียนได้ดังนี้

มันดูดีขึ้นนะ
แถมไม่มีตัวแปรระดับ package อีก

แต่ถ้าต้องการให้ package นี้เป็น public API สามารถทำให้ดีกว่านี้ได้อีก

นั่นคือการสร้าง interface ขึ้นมา
เพื่อไม่ให้เกิดการผูกมัดกันเกินไป
แถมยังช่วยให้ทดสอบด้วยการ mock dependency ต่าง ๆ ง่ายขึ้นอีกด้วย
เขียนได้ดังนี้

สังเกตไหมว่า

เมื่อเหล่า constructor และ function ต่าง ๆ
มีหน้าที่การทำงานและขอบเขตที่ชัดเจน
กำหนด dependency ต่าง ๆ อย่างชัดเจนแล้ว
เราไม่จำเป็นต้องมี global variable หรือ variable ในระดับ package อีกต่อไป
เป็นการลด magic ต่าง ๆ ลงไป
ผลที่ตามมาคือ
การ refactor ที่ง่ายขึ้น
การดูแลรักษา code ง่ายขึ้น

ดูเพิ่มเติมสำหรับการเขียนตามแนวทางนี่จาก Go kit

วันนี้ Go developer เขียน code กันอย่างไร ?
มัน simple ตามแนวคิดของภาษาหรือเปล่านะ ?