Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญ State Machine ใน Ruby: คู่มือปฏิบัติจริง

State Machine สามารถเก็บ State (สถานะ) ทั้งหมดที่เป็นไปได้ของ สิ่งใดสิ่งหนึ่ง และ Transition (การเปลี่ยนแปลงสถานะ) ที่อนุญาตระหว่าง State เหล่านั้น ตัวอย่างเช่น State Machine ของประตูจะมีแค่ 2 State (open และ closed) และ 2 Transition (opening และ closing)

แต่ State Machine ที่ซับซ้อนอาจมีหลาย State ที่แตกต่างกัน พร้อม Transition ร้อยๆ ระหว่างกัน จริงๆ แล้วถ้าคุณมองดูรอบๆ จะพบ Finite State Machines ล้อมรอบอยู่ทุกที่ — เมื่อคุณซื้อของบนเว็บไซต์ หยิบขนมจากเครื่องจ่ายอัตโนมัติ หรือแม้แต่ถอนเงินจาก ATM

ในบทความนี้ เราจะดูวิธีตั้งค่า State Machine ใน Ruby และใช้ Gem state_machines

State Machine ในการพัฒนา

เราต้องการ State Machine ในการพัฒนาเมื่อไหร่? คำตอบง่ายๆ คือเมื่อคุณต้องการจำลองกฎหลายอย่างสำหรับ State Transition หรือทำ Side-effect บน Transition บางอย่าง จุดสำคัญคือการระบุส่วนของแอปพลิเคชันที่จะได้ประโยชน์จาก State Machine ตัวอย่างที่ดีและใช้ได้ตลอดคือ Order ในบริบทของแอป E-commerce

สำหรับแอปขายสินค้าออนไลน์ทั่วไป Order อาจอยู่ใน State ต่อไปนี้:

  • created
  • processing
  • ready
  • shipped
  • delivered
  • void

คุณสามารถเห็น Transition ที่อนุญาตใน Diagram ต่อไปนี้

เชี่ยวชาญ State Machine ใน Ruby: คู่มือปฏิบัติจริง

การมอง Order ในรูป State Machine ทำให้เราเข้าใจ Flow ทั้งหมดตั้งแต่สั่งสินค้าจนถึงการจัดส่งได้ชัดเจน และสำหรับเรา Developer มันให้ขั้นตอนที่ชัดเจนว่า สิ่งไหนทำได้/ไม่ได้ ที่จุดต่างๆ ใน Flow นั้น

ด้วยนั้น ง่ายมากที่จะหลงทางลง Rabbit Hole ด้วยการนำไปใช้กับปัญหากว้างเกินไป และสุดท้ายได้หลายร้อย State ที่ยากต่อการเข้าใจและติดตาม ดังนั้นควรมีไอเดียภาพรวมและ State Chart สำหรับ State Machine ที่เสนอก่อนจะ Implement

State Machine แรกของเราใน Ruby

ลอง Implement OrderStateMachine ด้วย Ruby


Implementation นี้เรียบง่ายพอ สำหรับแต่ละ Transition ที่เป็นไปได้ใน State Machine เรากำหนด Method ใหม่ที่ทำ Sanity Check และ Perform Transition

จุดดีของ Implementation ข้างบนคือทุกอย่างชัดเจน (Explicit): Developer ใหม่เข้าใจขอบเขต State Machine ได้เร็วมาก

ลองดูการเพิ่ม Side-effects เข้า Transition เราจะ Ship Order อัตโนมัติเมื่อ Packed และ Deliverable ได้:


แม้ Implementation แบบ Naïve จะทำงานได้ดี แต่มัน Verbose (冗长) เกินไป โดยเฉพาะเมื่อมี Transition และ Condition มาก

เช็คเร็วบน Ruby Toolbox จะพบ Gem หลายตัวสำหรับ State Machine state_machines และ aasm เป็นที่นิยมที่สุด ทั้งคู่มี ActiveRecord Adapter หากต้องการใช้กับ Rails ทั้งคู่ทดสอบครบถ้วนและ Production-ready แล้ว ลองดูถ้าคุณต้อง Implement State Machine

ใช้ Gem state_machines ใน Ruby

ในบทความนี้ ผมจะอธิบายการ Model State Machine สำหรับ Order ด้วย Gem state_machines


Class ด้านบนกำหนด State Machine เรียบง่ายและ Transition ทั้งหมดที่เป็นไปได้ นอกจากนี้ยัง Expose Utility Methods มากมายให้ Order อัตโนมัติ:



Execute Side-Effects ด้วย Gem state_machines ใน Ruby

เช่นปกติในระบบจริง Transition หลายอย่างใน State Machine จะมาพร้อม Side-effects Gem state_machines ทำให้กำหนดและ Execute ได้ง่าย มาเพิ่ม Side-effects 2 อย่าง:

  1. บน Transition ทั้งหมดไป ready สร้าง Delivery Consignment หาก Order deliverable
  2. บน Event fail_delivery ทั้งหมด ส่ง Email แจ้งเตือน User เกี่ยวกับ Event

เราสามารถ Perform Transition ตามปกติใน Order:


แนะนำให้เช็ค Github Page ของ Gem state_machines เพื่อเรียนรู้ Options ทั้งหมดที่ Gem นำเสนอ

ใช้ Gem state_machines กับ ActiveRecord ใน Ruby on Rails

เช่นที่พูดไว้ ทั้ง state_machines และ aasm รองรับ ActiveRecord ตอนใช้ Gem state_machines กับ ActiveRecord ไม่มีอะไรเปลี่ยนแปลงมากกับ Implementation คุณยังคงใช้สิ่งที่พูดไปในส่วนก่อนหน้านี้ได้ส่วนใหญ่

นี่คือ Feature เพิ่มเติมที่คุณได้:

  1. Side-effects เกิดขึ้นภายใน Transaction หมายความว่าถ้าคุณทำ Database Operations ภายใน Transition Hooks และ Transaction ล้มเหลว State Change จะไม่ถูก Commit ดู use_transactions หากต้องการปิดพฤติกรรมนี้
  2. เหมือน ActiveRecord Callbacks ทั้งหมด หากคุณต้องการ Update Attribute ของ Model ปัจจุบันจาก Transition คุณต้องทำภายใน Callback before_transition เพื่อ Update Attribute ภายใน after_transition คุณต้อง Save Model อีกครั้ง ดูตัวอย่าง:

  1. Gem ให้ Scopes อัตโนมัติเพื่อ Filter Model ตาม State เช่น Order.with_state(:processing) หา Order ที่กำลัง Process หรือ Order.without_state(:processing) หา Order ที่ไม่ได้ Process
  2. หากพยายามเปลี่ยน State โดยไม่มี Transition ที่ตรงกัน (เช่นจาก processing ไป delivered) Record จะ Save ไม่ได้ และ Error จะถูกเพิ่มใน Validation Errors หากต้องการ Internationalize Error Messages ที่สร้างขึ้น ให้เพิ่ม Key ให้ Translation สำหรับ States และ Events ใน Config Files (เช่น en.yml):

Side note: ดูบทความนี้เพื่อ Troubleshoot ปัญหาประสิทธิภาพ ActiveRecord

สรุป: สร้าง State Machines ใน Ruby

ในบทความนี้ เราสำรวจว่าทำไมต้องใช้ State Machine ในการพัฒนา ก่อนสร้าง State Machine เรียบง่าย สุดท้ายดูการใช้ Gem state_machines ใน Ruby และ Ruby on Rails

ตอนนี้คุณเข้าใจ State Machine พื้นฐานแล้ว มั่นใจว่าคุณจะจดจำสถานการณ์หลายอย่างรอบตัวที่ใช้ State Machine อยู่แล้ว หรือจะได้ประโยชน์มากจากมัน

จนกว่าจะเจอกันใหม่ ขอให้โชคดีกับการสร้าง State Machines!

P.S. หากต้องการอ่านบทความ Ruby Magic ทันทีที่เผยแพร่ สมัคร Newsletter Ruby Magic ของเราและจะไม่พลาดบทความเด็ดๆ ไป!

เชี่ยวชาญ State Machine ใน Ruby: คู่มือปฏิบัติจริง

Pulkit Goyal

ผู้เขียน אורח Pulkit เป็น Senior Full-stack Engineer และ Consultant เวลาว่างเขียนประสบการณ์บนบล็อกตัวเอง

บทความทั้งหมดโดย Pulkit Goyal