July 13, 2015

ทำความรู้จักกับ guard ใน Swift 2

คำสั่ง guard เป็นของใหม่ที่ถูกเพิ่มเข้ามาใน Swift 2 และถูกเปิดตัวใน Apple's Platform State of the Union

จริงๆ แล้ว guard ก็คล้ายกับคำสั่ง if ที่ใช้ตัดสินใจว่าจะทำงานหรือไม่ทำงานโดยอิงกับ boolean ที่ส่งไปให้ตรวจสอบ แต่สิ่งที่ต่างกันชัดเจนก็คือโค้ดที่อยู่ในบล็อกคำสั่ง guard จะทำงานก็ต่อเมื่อค่าความจริงที่ทดสอบเป็นเท็จ ซึ่งวิธีตัดสินใจแบบนี้ก็จะคล้ายกับ assert นั่นเอง

เปรียบเทียบเทคนิคปกติที่ใช้กันอยู่


นี่คือท่าปกติที่หลายคนมักใช้ตรวจสอบว่าค่าที่ส่งเข้ามาในฟังก์ชั่นมีค่าตรงตามต้องการหรือไม่ หากไม่ใช่สิ่งที่ต้องการ ก็จบฟังก์ชัน ไม่ต้องทำอะไร แต่จริงๆ แล้วทำแบบนี้จะพบว่ามีจุดสังเกตอยู่ 2 จุด ได้แก่
  1. เรากำลังตรวจสอบโดยใช้เงื่อนไขของสิ่งที่เราไม่ต้องการ ไม่ได้ใช้เงื่อนไขของสิ่งที่เราต้องการมาตรวจสอบ (ส่วนใหญ่แล้วเรามักตรวจสอบกันแบบไหนเอ่ย?)
  2. หากต้องการใช้ตัวแปรที่ถูกตรวจสอบว่าใช้งานได้แล้ว เราจำเป็นต้องบังคับ unwrap optional value ก่อนจะนำมันไปใช้

จากตัวอย่างโค้ดด้านบน Swift ยังมีอีกทางที่ทำให้เราไม่จำเป็นต้อง unwrap optional value ก่อน ก็สามารถใช้ได้เลย แบบนี้


ตัวอย่างนี้ เราตรวจสอบโดยใช้เงื่อนไขของสิ่งที่เราต้องการ และไม่จำเป็นต้อง unwrap optional value ก่อนใช้งาน ยังไม่พออีกหรือ?

สิ่งที่เราต้องการทำจริงๆ ก็คือ เราไม่อยากเอาโค้ด logic การทำงานหลักของเราไปวางไว้ใน block ที่ใช้ตรวจสอบเงื่อนไข ลองคิดดูว่าหากมีการตรวจสอบเงื่อนไขหลายๆ อัน แน่นอนว่าโค้ดคงวางซ้อนๆ กันจนทำให้อ่านยากแน่นอน (ลองนึกนึกถึงว่าจะมีปีกกาตอนปิด block จะซ้อนกันขนาดไหน และโค้ดที่เป็น main logic จริงๆ คงถูก indent ไปยาวๆ) หรือจะใส่เงื่อนไขหลายๆ อันไว้ในการตรวจสอบครั้งเดียว แต่นั่นอาจทำให้เราไม่รู้ได้ว่า สรุปแล้วตัวแปรที่ถูกส่งเข้ามาให้ตรวจสอบ ไม่เป็นไปตามเงื่อนไขไหนกันแน่

ดังนั้นสิ่งที่เราอยากได้ก็คือ มีโค้ดสำหรับตรวจสอบสิ่งที่เราต้องการไว้ข้างหน้า ทุกเงื่อนไขก่อนที่จะรัน Logic หลักของฟังก์ชัน ซึ่งถ้าไม่ตรงตามต้องการ ก็จบฟังก์ชัน และด้วยรูปแบบนี้จะทำให้เราอ่านโค้ดได้เข้าใจมากขึ้นว่า input ลักษณะไหนล่ะ ที่จะทำให้ฟังก์ชันนี้ทำงาน

รูปแบบการตรวจสอบที่กล่าวมาข้างต้น ผมได้ยินหลายที่เรียกรูปแบบนี้ว่า Bouncer Pattern ซึ่งชื่อก็ถือว่าค่อนข้าง make sense เลยทีเดียว (Bouncer คือคนที่ยืนอยู่หน้าประตูเข้าร้าน ใครนิสัยไม่ดีเราจะจับโยนออกนอกร้านไป) และนี่เอง Swift ก็มีคำสั่ง guard มาให้เราใช้ แบบนี้ (ชื่อคำสั่งก็ยังดู make sense เลย!)


จากโค้ดด้านบน การใช้ guard จะช่วยให้เราสามารถใช้ pattern ได้ดังนี้
  1. ตรวจสอบโดยใช้เงื่อนไขของสิ่งที่เรา "ต้องการ" และวิธีคิดคล้ายกับ assert ที่ถ้าไม่ตรงตามเงื่อนไขของสิ่งที่เราอยากได้ โค้ดในบล็อก else จะทำงาน (จับมันโยนออกไป!)
  2. ถ้าเป็นไปตามเงื่อนไขที่เราต้องการแล้ว optional value จะถูก unwrap ให้เราใช้อัตโนมัติใน scope เดียวกันกับที่คำสั่ง guard ถูกเรียกใช้ ในกรณีนี้ก็คือใช้ได้ในฟังก์ชัน fooGuard(_:) ข้อนี้เราจะเห็นประโยชน์ของคำสั่ง guard ว่ามันมีประโยชน์ชัดเจนเลยล่ะ
  3. เราตรวจสอบกรณีที่ไม่พึงปราถนาได้ตั้งแต่ช่วงแรกของฟังก์ชัน ทำให้เราอ่านโค้ดได้ง่าย และดูแลรักษาง่ายอีกด้วย
ในกรณีที่ input ไม่ใช่ optional value แล้ว ทั้ง 2 ฟังก์ชันนี้จะทำงานเหมือนกันเลย ทั้งแบบใช้ guard และไม่ใช้ guard


เนื้อหาเต็มอ่านมาจากที่นี่ http://ericcerney.com/swift-guard-statement/