จาก course Pratical Go ที่ไปเรียนมานั้น
มีเรื่อง Package design หรือการออกแบบหรือวาง package
ในระบบที่พัฒนาด้วยภาษา Go ว่า
มีแนวคิดและอย่างไร
ออกแบบอย่างไร
รวมทั้งหน้าที่ความรับผิดชอบ มาดูกันว่ามีอะไรบ้าง

หน่วยที่เล็กมาก ๆ ของ software น่าจะเป็นสิ่งที่เรียกว่า Unit

ซึ่งมันมีขนาดเล็ก เป็นอิสระและไม่สามารถแยกออกมาได้อีก
แต่ในการทำงานจริง ๆ นั้น
คำว่า Unit มันอาจจะมีความซับซ้อนมากมายอยู่ข้างใน
ดังนั้นบ่อยครั้งเราไม่สามารถไปดูว่า
การทำงานภายในหรือ implementation เป็นอย่างไร
เนื่องจากมีหลายสิ่งอย่าง รวมทั้งมีการเปลี่ยนแปลงอย่างสม่ำเสมอ
แต่เราสามารถดูพฤติกรรมการทำงานจากภายนอกได้ (behaviour)
ดังนั้นแนวคิดการออกแบบ package ของภาษา Go ก็เช่นกัน
นั่นคือ ให้ออกแบบ package ตามพฤติกรรมของการทำงานนั่นเอง

โดยในภาษา Go นั้นจะมองว่า

สิ่งที่เล็กสุดหรือ Unit นั้นคือ package ไม่ใช่ function/method หรือ class นะ
ดังนั้นแต่ละ package จะให้ความสนใจเพียงว่า
แต่ละ package ทำงานอะไรบ้าง มีอะไรให้ใช้บ้าง (Public APIs)
นั่นคือสนใจที่ What ไม่ใช่ How นะ
มาดูแนวคิดในการออกแบบ Package กัน

แนวคิดที่ 1 การตั้งชื่อ package ที่ดี

เริ่มด้วยเรื่องที่ยากมาก ๆ เลย
ถ้าตั้งชื่อผิดนี่เหมือนการเริ่มเดินที่ผิดพลาดมาก ๆ
ต้องตั้งชื่อที่บอกว่า package นี้
ทำอะไรได้บ้าง
มีเป้าหมายอะไรบ้าง
ไม่ได้บอกรายละเอียดข้างในนะ ข้อนี้ยากจริง ๆ

แนวคิดที่ 2 ชื่อที่ดีมันต้อง unique

นั่นคือชื่อที่พูดกออกมาแล้วรู้ทันทีว่าคืออะไร ทำงานอะไร
แต่ก็ต้องระวังชื่อ package ที่มันกว้างเกินไป
เช่น shared, client, worker เป็นต้น
ซึ่งทำให้งงว่า มันคืออะไร และทำอะไรกันแน่
หรืออาจจะมีชื่อไปทับซ้อนกับ package อื่น แบบนี้ก็ไม่ไหว
แต่ถ้าเกิดขึ้นมาก็ความแก้ไขด้วยการย้ายไปรวมกันเป็นต้น

แนวคิดที่ 3 หลีกเลี่ยงการใช้ชื่อพวก base, commom และ util

ใครบ้างที่เคยตั้งชื่อแบบนี้ ?
ยกมือขึ้น
น่าจะผ่านมือมาแล้วกันทุกคนหรือเปล่านะ !!

เป็นชื่อ package ที่แย่มาก ๆ
เพราะว่าไม่ได้ระบุแบบชัดเจนว่ามันทำอะไร
ผลที่ตามมาคือ มี code จำนวนมากอยู่ใน package เหล่านี้
และยากต่อการอธิบายอีก
ลำบากนะ
หนักไปกว่านั้น
อาจจะก่อให้เกิดการเรียกใช้งานเป็น cyclic อีกด้วย ใครเคยบ้าง ?

ดังนั้นสิ่งเหล่านี้คือ การออกแบบที่แย่และผิดพลาดมาก ๆ

แก้ไขด้วย ทำการย้ายสิ่งที่อยู่ package เหล่านี้
ไปรวมกับจุดที่เรียกใช้งานมันเถอะนะ
บางครั้งอาจจะบอกว่า
การทำแบบนี้มันทำให้เกิด code ที่ซ้ำซ้อนขึ้นมามากมายสิ
ใช่แล้วนะ
แต่มันดีกว่าการ import ไปมา ๆ แล้วทำให้เกิดปัญหาขึ้นมา

ในภาษา Go นั้นจะแยกพวก helper หรือ util function
ไปไว้ใน package นั้น ๆ เลย
แต่ถ้ามันเยอะก็จะสร้าง package helper มาเช่น
ตัวช่วยในการจัดการ string ก็คือ package strings เป็นต้น

อีกตัวอย่างที่น่าสนใจคือ
การรวมการทำงานต่าง ๆ ไว้ใน package เดียวกัน
แต่ให้แยกเป็นไฟล์ใน package นั้น
ยกตัวอย่างเช่น package net/http จะไม่มี package ย่อย เช่น client และ server
แต่ใน package net/http จะมีไฟล์ client.go และ server.go แทน
ซึ่งแต่ละไฟล์ก็มีหน้าที่การทำงานที่แตกต่างกันไป

แนวคิดที่ 4 ในแต่ละ function ควร return ให้เร็วที่สุด

ลดความซับซ้อนของ code ลง ให้ทำการ return ให้เร็วที่สุด
เช่นทำการตรวจสอบ error ก่อน
ถ้าไม่มีก็ทำงานตาม success flow ต่อไป
ทำให้ code ไม่รกรุงรัก หรือ มีทั้งการตรวจสอบและทำงานจริง ๆ อยู่ปนกัน
ดังนั้นควรแยกการทำงานเป็นส่วน ๆ ให้ชัดเจน

เพราะว่า code ที่เราเขียนขึ้นมานั้น เป้าหมายหลัก ๆ คือ
ความชัดเจน เข้าใจง่ายและเพิ่ม productivity
ดังนั้น code มันควรเข้าใจได้ง่าย ไม่รกรุงรังนั่นเอง
หนึ่งในแนวคิดที่อ้างอิงถึงคือ Line of side in code ของคุณ Mat Ryer

แนวคิดที่ 5 ควรใช้ประโยชน์จาก Zero value

โดยคุณ Dave Cheney ได้เขียนอธิบายไว้ใน blog ชื่อว่า 
What is the zero value, and why is it useful?

แนวคิดที่ 6  หลีกเลี่ยงการจัดการ state ใน package level

หัวใจหลักของการออกแบบ software ที่ดูแลจัดการได้ง่าย
คือการออกแบบ loose coupling
หรือทำให้แต่ละส่วนไม่ผูกมัดกันมากหรือให้น้อยสุด ๆ
ในภาษา Go ก็คือ ถ้าเกิดการเปลี่ยนแปลงใน package หนึ่ง ๆ แล้ว
ต้องไม่กระทบกับส่วนการทำงานที่อื่นหรือ package อื่น ๆ

สามารถทำได้ 2 วิธีการคึอ

  • ใช้ interface ในการอธิบายพฤติกรรมการทำงานของ package
  • ลดหรือหลีกเลี่ยงการใช้ Global state หรือ package level ลง

ต่อไปจะมาดูเรื่อง project structure กัน

โดยทั้งสองเรื่องนี้ทางผู้สอนจะเน้นมาก ๆ
เพราะว่าเป็นเรื่องที่สำคัญมาก ๆ ทั้งมือใหม่และเก่า
รวมทั้งมีการพูดคุยกันเยอะมาก ๆ
นี่คือหนึ่งแนวทางที่น่าจะทำให้ชัดเจนขึ้น