การเปลี่ยน JavaScript if-else และ try-catch ให้เป็นรูปแบบ Functional
16-ส.ค.-19
คัมภีร์เทพ IT
สำหรับ Programmer เชื่อว่าคงคุ้นเคยหรือใช้งานคำสั่ง if-else และ try-catch มาบ้างแล้ว โดยในบทความนี้จะกล่าวถึง การเปลี่ยน if-else และ try-catch ใน JavaScript ให้เป็นรูปแบบ Functional
“การสร้างรูปแบบ Imperative ในภาษา (If-else/Try-catch) ให้สามารถ Declarative ได้มากขึ้น จะช่วยปรับปรุงความสามารถในการอ่านและ Test Code ของคุณ” แม้มันจะเป็นความเห็นที่ได้ชัดเจน แต่ก็ไม่ควรไปยึดถือมันมากนักเพราะมันก็ต้องใช้อย่างระมัดระวัง
Abstractions นั้นอาจเป็นเรื่องยาก เพราะมันบังคับให้คุณและทีมของคุณต้องทำตามความเห็นของคนส่วนใหญ่ และการที่จะให้ทุกคนเห็นด้วยนั้นเป็นเรื่องยาก โดยเฉพาะอย่างยิ่งเมื่อคุณพยายามที่จะ Rewrite รูปแบบภาษาที่เรียบง่ายให้เป็น Functional Abstractions และมันก็ยิ่งยากอีกเท่าตัว เนื่องจากยังมีประเด็นเรื่องการอ่านง่ายของ Code และ Level ของ Unit Test เข้ามาเกี่ยวข้อง ซึ่งเรื่องนี้ถือเป็นเรื่องความรู้สึกส่วนบุคคล
Functional construct #1: conditionally
เรามาลองพิจารณาการแปลงพื้นฐานอย่าง If-else กัน โดยจะเป็นอย่างไรถ้าเราจะทำให้ If-else เป็นในรูปแบบของ Functional?
การ Implementation จะมีลักษณะดังนี้:
คำถามที่น่าสนใจคือ เราจะสามารถเขียน If-else ในรูปแบบที่ ไม่ว่าจะเงื่อนไข True หรือ False ที่จะ Return ค่าด้วยการ Evaluate Function ได้หรือไม่? หรือพูดอีกอย่างก็คือ ถ้าเราสามารถแสดงถึงสิ่งที่ If-else ทำใน Pure Function ได้ มันจะมีลักษณะอย่างไร?
มันต้องใช้การ Config ซึ่งมี 3 Functions คือ If(), Then() และ Else() และมันสร้าง Function ที่สามารถรับ Props Argument ของคุณ
ถ้า if(props) มีการ Evaluate ค่าเป็น True มันจะไปสู่ then(props) หรือไม่ก็ else(props) ซึ่งทั้ง 3 Functions ได้รับ Input เดียวกันและสร้าง Result ประเภทเดียวกัน
หากคุณใช้ TypeScript เราสามารถบังคับ Input Type และ Result ด้วย Generics หากสิ่งนี้ดูซับซ้อนเกินไป หรือคุณไม่มีประสบการณ์เกี่ยวกับ Generics ใน TypeScript คุณสามารถข้ามตัวอย่างต่อไปนี้ได้
ลองพิจารณาเงื่อนไข If-else ดู:
ตัวอย่างข้างต้นเกือบเป็นวิธีการเขียนที่ Perfect แล้ว แต่เราสามารถทำได้ดีกว่านั้น ตอนนี้อยากให้ลองดูวิธีการเขียนด้วย conditionally ตามด้านล่างดู:
ตอนนี้ Concern ต่าง ๆ ถูกจัดการโดย 2 Functions ที่แตกต่างกัน ซึ่ง conditionally ได้บังคับให้คุณแยก Concern ออกจากกันอย่างค่อยเป็นค่อยไป ในทางกลับกัน นี่เป็นทางเลือกให้คุณได้ Test แต่ละ Concern แยกเป็นส่วน ๆ และ Conditionally เองก็ทำหน้าที่แบบนั้น ซึ่งเป็นไปตาม F.I.R.S.T principles ของ Unit Testing
เมื่อคนอื่นอ่าน Code ของคุณเพื่อทำความเข้าใจกับสิ่งที่ getCarConfig ทำ พวกเขาไม่จำเป็นต้องลงไปที่รายละเอียดการ Implement ของ priceChange และ getDescription เพราะคุณตั้งชื่อที่เข้าใจได้แล้ว ตอนนี้การแยกเป็นส่วน ๆ ของคุณทำหน้าที่เพียงอย่างเดียว(Single Responsibility) และการตั้งชื่อที่เหมาะสมจะเป็นการสร้างความประหลาดใจน้อยที่สุด(Least Astonishment) ให้กับคนที่ได้อ่านมัน
นั่นคือเหตุผลที่ฉันสนับสนุนการนำ Functional Programming มาใช้ใน JavaScript มันบังคับให้คุณแบ่งปัญหาออกเป็นส่วนเล็ก ๆ ที่เรียกว่า Function ซึ่ง Function เหล่านี้:
- แยก Concern ออกเป็นส่วน ๆ
- ช่วยให้การ Test ทำได้ดียิ่งขึ้น
- เป็นไปตามหลักการ Single Responsibility
- ด้วยการฝึกฝนเล็ก ๆ น้อย ๆ ในการตั้งชื่อสิ่งต่าง ๆ จะทำให้เป็นไปตาม หลักการของ Least Astonishment
Functional construct #2: tryCatch
Exceptions ถือเป็นเครื่องมือที่ทรงพลังในหลาย ๆ ภาษา พวกมันจะจัดการขอบเขตที่ ไม่รู้จัก (Unknown), ไม่สามารถหาเหตุผลได้ (Unreasonable) และ ไม่ปลอดภัย (Unsafe) ของ System
ใน JavaScript คุณสามารถใช้ Try-catch ได้ดังนี้:
หากคุณต้องการบันทึก State (เช่น storedSuccessfully) คุณต้องทำการ Declare เพื่อเป็นการส่งสัญญาณการ Mutation ของ State ที่เป็นไปได้เช่นเดียวกับในตัวอย่าง ตามความหมายแล้ว Try-catch จะเป็นสัญญาณการหยุดการควบคุม Flow และทำให้การอ่าน Code นั้นยากขึ้น
ลองมาสร้าง Functional Utility เพื่อลดปัญหาเหล่านั้นกัน:
จากตัวอย่าง เราทำการ Encapsulate โครงสร้างของ Try-catch ใน Function ซึ่ง tryCatch() จะรับ Config Object ด้วย 2 Functions จากนั้นจะ Return Function ซึ่งจะยอมรับ Single Props Object
- tryer(props) จะถูก Evaluate และ Return Result กลับมา
- ขณะที่ทำ tryer(props) หากมี Exception เกิดขึ้น catcher(props) ก็จะถูกเรียกใช้
ด้วย TypeScript คุณสามารถใช้ Generics เพื่อบังคับใช้ Input Type และ Output Type ของรูปแบบนี้
หากคุณรู้สึกกังวลในเรื่องการใช้ Generics เรามาลอง Refactor ตัวอย่างก่อนหน้านี้กัน
เราจะเห็นว่ารูปแบบ Functional ของเราบังคับให้เราแยกส่วนที่ไม่ปลอดภัยของ Code ออกเป็น Function ต่าง ๆ นอกจากนี้เราลงเอยด้วย 3 Functions ที่แยกจากกันโดยสิ้นเชิง ซึ่งเราสามารถ pipe() เข้าด้วยกันเพื่อให้ได้ Result ของเราออกมา
จากข้อดีที่ได้อธิบายไปก่อนหน้านี้ สามารถนำมาใช้ในส่วนนี้ได้เช่นกัน สิ่งที่สำคัญที่สุดคือ เรื่องความสามารถในการอ่าน ตอนนี้เมื่อมีคนมาอ่าน setUserLangage Function ของคุณ พวกเขาไม่จำเป็นต้องลำบากในการวิเคราะห์ Try-catch ล่วงหน้า เพราะมันถูก Encapsulate ด้วย storeLanguageCode Function
ปิดท้ายบทความ
ไม่สนับสนุนให้คุณใช้ conditionally และ tryCatch เพียงเพราะแค่เห็นว่ามันมีประโยชน์ บางครั้งการใช้ Ternary Operation ที่เรียบง่าย หรือ If-else ก็สามารถทำให้อ่านเข้าใจได้โดยง่าย แต่แนะนำให้คุณทำตามวิธีเดิม ๆ ที่ทำกันมา เพราะวิธีจะช่วยให้ Developer ใช้การตัดสินใจที่น้อยลงและไม่ต้องเหนื่อยกับการทำความเข้าใจมากเกินไป
ที่มา: https://itnext.io/
รับตำแหน่งงานไอทีใหม่ๆ ด้วยบริการ IT Job Alert
อัพเดทบทความจากคนวงในสายไอทีทาง LINE ก่อนใคร
อย่าลืมแอดไลน์ @techstarth เป็นเพื่อนนะคะ
บทความล่าสุด