หลังจากทำการ review code ของทีมพัฒนา
พบรูปแบบหนึ่งที่น่าสนใจสำหรับการตรวจสอบข้อมูลเข้า ( Input validation )  ก่อนที่จะส่งไปยังส่วนการทำงานอื่นๆ
เช่น Service, Business logic และ ส่วนคำนวณ เป็นต้น
โดยขั้นตอนการตรวจสอบส่วนใหญ่จะมีเยอะหรือเยอะมาก
ขึ้นอยู่กับ จำนวนข้อมูล กฎต่างๆ ที่ต้องทำสอบ ส่งผลให้เกิด code ใน if-statement เยอะมาก
ดังนั้นเรามาดูว่าจะลด code เหล่านี้ลงไปได้อย่างไร

ตัวอย่างการตรวจสอบข้อมูลมักประกอบไปด้วยขั้นตอนดังนี้

  • ตรวจสอบข้อมูลเป็นค่าว่างหรือไม่
  • ตรวจสอบข้อมูลอายุ เป็นตัวเลขหรือไม่
  • ตรวจสอบข้อมูลตัวเลขอยู่ในช่วงที่ต้องการหรือไม่
  • ตรวจสอบรูปแบบของ Email
  • ตรวจสอบว่า Doamin ของ Email นั้นได้รับอนุญาตหรือไม่

จากตัวอย่างนี้มักจะเขียน code ส่วนการตรวจสอบข้อมูลอยู่กับส่วนการทำงานต่างๆ ไปดังนี้

  • จากการเขียน code แบบนี้ถามว่าทำงานได้ไหม ตอบว่า ดี
  • จากการเขียน code แบบนี้ถามว่าทำงานดีไหม ตอบว่า ก็ดีนะ
  • จากการเขียน code แบบนี้ถามว่าจะปรับให้ดีขึ้นทำอย่างไร ตอบว่า ก็แยกออกมาสิ

ท่ามาตรฐานคือ การแยกออกมาเป็น method/function ใหม่
หรือมองว่ามันถูกใช้ซ้ำแน่ๆ หรือเริ่มมีการใช้ซ้ำ
ก็มักจะแยกออกมาเป็น class กลาง สำหรับทำการ Validate เลย

แต่การทำงานยังคงเป็นลักษณะเดิม คือ ตรวจสอบตาม IF statement ไปเรื่อยๆ
ซึ่งถ้ามองในเรื่องของ  Cyclomatic complexity 
แล้วจะพบว่า code ในส่วนการตรวจสอบข้อมูลมันจะมีค่าที่สูงมากๆ
ยิ่งถ้ามีข้อมูลที่ต้องตรวจสอบจำนวนเยอะๆ ด้วยแล้ว ….
ถ้าไม่เชื่อลองวัดค่า cyclomatic complexity ของ code ข้างต้นดีกว่า
โดยผมใช้ SonarQube มาช่วยวัด ได้ผลดังภาพ

คำอธิบาย จาก code ดังกล่าวมีค่าความซับซ้อน 15 ซึ่งสูงกว่าค่าปกติคือ 10

Screen Shot 2557-08-07 at 2.00.33 PM

ดังนั้น ตอนนี้เรามีหนี้ทางเทคนิคมาแล้ว ( Technical Debt ) เราจะแก้ไหมล่ะ ?

น่าจะเริ่มต้นด้วยการแยก code การตรวจสอบข้อมูลออกมาเป็น method ย่อย ชื่อว่า validate ก่อน
ส่วนถ้าจะลด if ลงมาล่ะ ก็คงแยกออกเป็น method ย่อยๆ แล้วมี method หนึ่งทำหน้าที่เรียกใช้งานไป
ก็พอได้นะ แต่ลองคิดดูว่า ทดสอบยากไหม ?

สิ่งหนึ่งที่บรรดา developer มักถูกบอกให้ทำก็คือ refactor code ตัวเองหรือของระบบดูสิ
โดย code ที่ได้จะต้อง อ่านง่าย เข้าใจง่าย ทดสอบง่าย และ ดูแลรักษาง่าย

… ในใจ developer คงคิดว่า แล้วทำไมมึงไม่เขียน code ให้มันเป็นไปตามนั้นตั้งแต่ต้นล่ะ
แต่ในความเป็นจริง ต้องก้มหน้าก้มตารับมาทำ ซึ่งทำให้ developer คนนั้นมองว่า การ refactor เป็นอะไรที่น่ากลัวสุดๆ
เพราะว่า จะทำอย่างไรล่ะเนี่ย บางคนแค่คำว่า refactor ยังไม่เคยได้ยินเลย !!!

จากปัญหานี้ เราจะแก้ไขอย่างไรดีล่ะ ?

วิธีการหนึ่งที่อยากแนะนำก็คือ หนึ่งใน SOLID นั่นก็คือ S = SRP = Single Responsibility Principle
คือ ในแต่ละ class/method ควรมีหน้าที่การทำงานเพียงอย่างเดียว
จากตัวอย่างนี้ก็คือ การตรวจสอบข้อมูลแต่ละอย่างนั่นเอง

โดยแยกส่วนการตรวจสอบข้อมูลออกมาเป็น class ย่อยๆ
เพื่อทำให้มันอ่านง่าย เข้าใจง่าย รวมทั้งดูแลรักษาได้ง่ายด้วย
แล้วมันทำอย่างไรกันล่ะ …

มาเริ่มกันดีกว่า ด้วยการเขียน class diagram ซึ่งผมเน้นว่าต้อง คิด ก่อน ทำ เสมอ

Screen Shot 2557-08-07 at 3.54.25 PM

ต่อไปลงมือเขียน code กันเลย เริ่มที่การสร้าง interface RegisterRule

จากนั้นสร้าง Rule ต่างๆ สำหรับการตรวจสอบข้อมูลของการสมัครสมาชิก

ประกอบไปด้วย

  • NameEmptyRule
  • NameFormatRule
  • EmailEmptyRule
  • EmailFormatRule
  • EmailDomainAllowRule
  • AgeAllowRule

ตัวอย่างเช่น

และ

จะเห็นได้ว่า เราแยกหน้าที่การทำงานแต่ละอย่างออกจากกันตามกฏของ SRP เลย

ปัญหาที่ตามมาก็คือ แล้วเราจะนำ Rule ต่างๆ เหล่านี้มาใช้งานอย่างไร

ง่ายๆ มาก นั่นคือ เราทำการแก้ไขการทำงานใน method register() ดังนี้

หลังจากทำการแก้ไขตามแนวคิด SRP แล้ว พบว่าค่าของ cyclomatic complexity ลดลง จาก 15 มาเป็น 2.8 ตามรูป

Screen Shot 2557-08-07 at 4.13.08 PM

แต่จาก code ที่เราเขียนขึ้นมานั้น สามารถทำให้ดีกว่านี้ได้อีกนะ

สามารถทำได้ด้วยการแยกส่วนการสร้าง Rule ต่างๆ ออกมาจาก method register() ซะ
เช่นการสร้างคลาส RuleFactory ขึ้นมาดังนี้

และใน method register แก้ไขดังต่อไปนี้

หลังจากทำการแก้ไขแล้ว พบว่าค่าของ cyclomatic complexity ลดลงจาก 2.8 มาเป็น 2.4 ตามรูป

Screen Shot 2557-08-07 at 4.20.22 PM

โดยสรุป

มาถึงตรงนี้น่าจะพอทำให้เห็นพัฒนาการของ code ตั้งแต่เริ่มต้นที่มีการตรวจสอบข้อมูล
ผ่าน IF-statement จำนวนเยอะมาก ซึ่งมันอ่านยาก แก้ไขยาก และ ทดสอบยาก (  ตามที่ผมคิด )

เมื่อเราทำการ refactor code ด้วยแนวคิด SRP แล้ว ก็น่าจะทำให้เห็นว่า code ที่ได้นั้น
เริ่มอ่านง่ายขึ้น ทดสอบง่ายขึ้น แยกหน้าที่การทำงานอย่างชัด ทำให้เราสามารถแก้ไขและดูแลรักษา code ได้ง่ายขึ้น

น่าจะพอทำให้เห็นหนึ่งแนวทางในการพัฒนา code ของเราให้ดีขึ้นกว่าเดิมครับ
ซึ่งยังมีวิธีการอื่นๆ อีกนะครับที่สามารถทำให้ code ดีขึ้นไปอีก ลองฝึกและนำไปใช้ดูกันครับ

ตัวอย่าง source code อยู่ที่ Github :: Up1 :: Demo Validate Data