อย่าเก็บข้อมูลด้วย Boolean Flag ถ้าไม่จำเป็น
เรื่องที่อยากแชร์ในบล็อคนี้เหมือนเป็น pain ของการ Maintain ซอต์ฟแวร์มาก ๆ ครับ คือเรื่องของการใช้ Boolean Flag ส่วนตัวที่นั่งคิดมา คาดว่าน่าจะมีปัญหาตั้งแต่การวิเคราะห์ Requirement และออกแบบที่ไม่ได้คิดถึงอนาคตมากนัก ทำให้เกิดปัญหาตามมาหลังจากพัฒนา Feature ไปเรื่อย ๆ ก็คือจะมี Boolean Flag เต็มไปหมดภายใน Entity นั้น ๆ สุดท้ายกลายเป็นว่าซอฟต์แวร์ Maintain ยากเพราะทำให้เกิดความซับซ้อนจากการที่ใช้ flag มาคิดเงื่อนไขเต็มไปหมดใน Feature ของเรา
ซึ่งบล็อคนี้พยายามพูดถึงการเก็บ flag เป็น 1 property นะครับ หรือ เก็บ boolean ใน 1 column database เยอะ ๆ ไม่ได้พูดถึงการเขียน boolean flag ใน variables ครับ
Boolean Flag คือ Property หนึ่งใน Entity เป็นประเภท boolean ที่เก็บ true หรือ false หรือมีค่าที่เทียบเท่ากับ bit ที่เป็นค่า 0 กับ 1 นั่นเองครับ ซึ่งส่วนมากเราก็ใช้กันเป็นปกติตามการเขียน condition ด้วย if กันบ่อย ๆ
มันเป็นเรื่องที่ทำให้ปวดหัวกับการเขียนโปรแกรมมาก ๆ เพราะทำให้โปรแกรมซับซ้อนมากขึ้น คิดง่าย ๆ คือ ถ้าเรามี 1 Flag แสดงว่ามีเงื่อนไขได้ 2 กรณี หากมี 4 ก็จะกลายเป็น 2⁴ หรือ 16 กรณี นอกจากเขียนเทสกระจุยกระจายแล้ว ยังทำให้การ Maintain ต่อมันยากมาก ๆ เพราะ 16 กรณีนี้เป็นสิ่งที่ไม่ควรจะไปแตะต้องเลยทีเดียว วันใดวันหนึ่ง Requirement เปลี่ยนก็คือต้องลง Effort มหาศาลกับแค่ Feature เดียว หรืออาจจะแค่ Function เดียวด้วยซ้ำครับ
ตัวอย่าง
- ต้องพัฒนา Feature การทำ Promotion แบบไม่จำกัดจำนวน
- สามารถตั้งค่าช่วงเวลาได้
- สามารถให้เจ้าของสินค้าปิดการใช้งานได้
ลองวิเคราะห์จะได้ว่าการทำโปรโมชัน น่าจะมี flag หลายอย่างมากดังนี้
- เช็คว่าโปรนี้เปิดหรือปิด
- เช็คว่าโปรนี้เห็นก่อนถึงเวลาหรือไม่
- เช็คว่าโปรนี้อยู่ในช่วงเวลาหรือไม่
- เช็คว่าโปรนี้หมดอายุหรือไม่
ถ้าเราออกแบบด้วย Boolean Flag จะทำให้เกิด flag 4 ตัวคือ
- isEnabled (เปิด/ปิด โดยเจ้าของสินค้า)
- isComingSoon (ยังไม่ถึงเวลาโปรโมชัน)
- isAvailable (อยู่ในช่วงเวลาโปรโมชัน)
- isExpired (หมดอายุ)
กรณีที่โค้ดมันไม่ได้ใช้ต่อ หรือ ไม่ได้ทำ Feature นี้ต่อจะไม่มีปัญหาครับ แต่ปัญหาจะเกิดเมื่อ อยากทำ Feature นี้ต่อโดยอยากให้โปรโมชันมีกำหนด limit ต่อวันได้ด้วย จะเห็นว่ามันเริ่มทำต่อได้ยากแล้ว พูดง่าย ๆ คือ closed to extensions และ hard to modify ส่วนทางกับ SOLID Principal แบบ 100%
จากที่ผมลองไปหาดูก็มีหลายคนที่เจอ Pain นี้ครับ ซึ่งวิธีแก้ปัญหาก็จะเหมือน ๆ กันคือ อย่างง่ายที่สุดให้ใช้ enum มาช่วยครับ แต่ถามว่าจำเป็นต้องใช้ enum ไหมมันก็จะมีกรณีที่บางภาษาอาจจะทำไม่ได้ก็อาจจะทำเป็นค่า constant เอาไว้ เพราะวัตถุประสงค์คืออยากให้เป็นสิ่งที่ประกาศให้ชัดเจนว่ามี state อะไรบ้างที่จะสามารถเกิดขึ้นได้กับ entity นี้ โดย state ทั้งหมดนั้นควรจะจะเป็นแค่ property เดียวของ entity นั้นไปเลย พูดง่ายๆ ก็คือ เก็บสถานะของ entity ไว้ใน column หนึ่งนั่นเอง อาจจะเป็น status หรืออะไรก็ได้ครับ อย่าให้มันเกิด flag ใหม่ เพราะถ้าเป็น flag ใหม่ มันก็คือ column ใหม่
จากตัวอย่างข้างต้น หากเราย้ายมาใช้ enum มันก็จะมีหน้าตาประมาณนี้คือ
enum PromotionStatus {
ENABLED,
COMING_SOON,
AVAILABLE,
EXPIRED;
}
แต่สุดท้ายการทำแบบนี้ก็ยังผิดหลัก SOLID อยู่ดีเพราะ ข้อ Open-closed priciple ไม่อยากให้เราแก้ไขโค้ดเดิม แต่การแก้ enum มันก็คือการแก้ซึ่งหากให้ตรงตามหลักจริงๆ ผมก็นึกถึง Design Pattern ตัวหนึ่งคือ State Design Pattern (SDP) นั่นเอง ขออธิบายคร่าว ๆ นะครับ คือทำให้การเพิ่ม state ของ entity ได้ง่าย เพราะทุก state แยกคลาส และ มีหน้าที่ของตัวเองชัดเจน ซึ่งมันสามารถทำได้ครับ
แต่ส่วนตัวเอนเอียงไปในทางของ enum เพราะ Simple กว่ามาก ๆ เวลาพัฒนาซอฟต์แวร์ขนาดใหญ่เนื่องจากหากมี State ใหม่เราก็เพียงแค่เพิ่มใน enum และสามารถนำไปใช้ได้เลย กลับกันหากใช้ SDP มันอาจจะตรงตามทฤษฏี แต่การพัฒนาต่ออาจจะไม่ได้ง่ายอย่างที่คิด เพราะการใช้ SDP บังคับให้เราสร้าง class ใหม่ ซึ่งเวลาเราไปตามหาว่ามี State อะไร เราจะต้องไปไล่ตามหาว่ามีกี่ไฟล์ ซึ่งการใช้ enum จะ simple เพราะ readable และสามารถทำได้ไวมากกว่าครับ
นอกนั้นยังมีเรื่องความรู้ของ Design Pattern ที่อาจจะเป็นปัญหาเล็กน้อยหากไม่เคยมีความรู้แต่ต้องมาทำโค้ดส่วนนี้ต่อ คือก็ต้องไปเรียนรู้เพิ่มทำให้ใช้เวลาทำมากขึ้น แต่หากเป็น enum มันเป็น tool ของตัวภาษาอยู่แล้วเราไม่จำเป็นต้องทำความเข้าใจมากก็สามารถทำต่อจากโค้ดเดิมที่เคยมีคนพัฒนาไว้ได้เลย
สรุป
อยากให้บล็อคนี้เป็นข้อเตือนใจไว้ทั้งตัวเองและคนที่ผ่านมาอ่านครับ สำหรับสิ่งที่ทำพลาดแล้ว ก็อาจจะไป refactor / rewrite ได้หากมี effort พอ แต่อยากให้เก็บเป็นข้อระวังไว้หากเราต้องพัฒนา feature ใหม่ ๆ ให้คิดให้ดีและมองถึงอนาคตว่ามีโอกาสที่เราจะต้องกลับมาปรับจุดที่เป็น boolean flag อันนี้หรือไม่ หากมีก็ทำเผื่อให้มันต่อยอดได้ ซึ่งกรณีนี้ผมว่ามันยากที่จะพัฒนาต่อครับ ควรทำเป็น enum อย่างที่บอกครับ
ย้ำอีกรอบหนึ่ง บล็อคนี้พยายามพูดถึงการเก็บ flag เป็น 1 property นะครับ หรือ เก็บ boolean ใน 1 column database เยอะ ๆ ไม่ได้พูดถึงการเขียน boolean flag ใน variables ครับ
หากบล็อคนี้ขาดตกหล่นตรงไหนสามารถแจ้งและพูดคุยกันได้นะครับ
ยินดีรับฟังและแก้ไขครับ ❤
ขอบคุณทาง depa และ คณะเทคโนโลยีสารสนเทศ มจธ.
ที่มอบทุนการศึกษาเพชรพระจอมเกล้าเพื่อพัฒนาเทคโนโลยีและนวัตกรรมดิจิทัล และทำให้ได้ความรู้เพิ่มสำหรับใช้ทำงานจริงครับ
เรื่องต่อไปจะเป็นเรื่องอะไร ติดตามกันต่อไป ขอบคุณที่อ่านจนจบครับ 🙏