วันนี้ คุณจะได้เรียนรู้เกี่ยวกับเครื่องจักรของรัฐ วิธีทำงาน และวิธีใช้ในโครงการ Ruby ของคุณด้วยอัญมณี AASM
ตอนนี้ :
ลองนึกภาพสัญญาณไฟจราจร…
อาจเป็นสีแดง สีเขียว หรือสีเหลืองก็ได้
เมื่อเปลี่ยนสี ถัดไป สีขึ้นอยู่กับ ปัจจุบัน หนึ่ง.
สมมติว่านี่เป็นเสียงที่คนตาบอดจะได้รู้ว่าจะข้ามได้เมื่อไร
ตอนนี้ :
คุณกำลังเขียนซอฟต์แวร์สำหรับสิ่งนี้
คุณจะรู้ได้อย่างไรว่าต้องเล่นเสียงอะไรทุกครั้ง &ต่อไปจะเป็นสีอะไร?
คุณสามารถเขียนประโยค if แบบนี้ได้:
if @light.state == "green" @light.play_green_sound end if @light.state == "green" @light.change_to_yellow end # ...
รหัสการตรวจสอบสถานะนี้จะมีอยู่ทั่วไป!
เราจะปรับปรุงสิ่งนี้ได้อย่างไร
หากคุณใช้หลักการออกแบบเชิงวัตถุ คุณจะค้นพบรูปแบบการออกแบบของรัฐอีกครั้ง
รูปแบบการออกแบบของรัฐคืออะไร
รูปแบบการออกแบบของรัฐเป็นวิธีหนึ่งในการปรับใช้เครื่องของรัฐ
คุณต้องมี 3 องค์ประกอบ :
- A
Context
class คลาสนี้รู้ว่าสถานะปัจจุบันคืออะไร - A
State
class คลาสนี้กำหนดวิธีการที่ควรจะดำเนินการโดยแต่ละรัฐ - ชั้นหนึ่งสำหรับทุกรัฐ คลาสเหล่านี้จะสืบทอดมาจาก
State
คลาส
ในตัวอย่างสัญญาณไฟจราจรของเรา บริบทคือ TrafficLight
นั่นเอง
และสถานะเป็น Green
, Red
&Yellow
.
ทุกรัฐจะได้รู้ว่าต้องทำอย่างไร
ประโยชน์มหาศาล ?
ทุกรัฐรู้ตัวเองจึงไม่จำเป็นต้องตรวจสอบสถานะปัจจุบัน ซึ่งแปลเป็นประโยคที่มีเงื่อนไขน้อยกว่าซึ่งมักเป็นที่มาของความซับซ้อน
การติดตั้งสัญญาณไฟจราจร
มาดูโค้ดสำหรับการใช้งานจริงของรูปแบบนี้กัน
นี่คือ TrafficLight
:
class TrafficLight def initialize @state = nil end def next_state(klass = Green) @state = klass.new(self) @state.beep @state.start_timer end end
นี่คือฐาน State
:
class State def initialize(light) @light = light end def beep end def next_state end def start_timer end end
ใช่ 3 วิธีนี้ว่างเปล่า
เป็นธรรมเนียมปฏิบัติทั่วไปในภาษาการเขียนโปรแกรมอื่นๆ (เช่น Java) เพื่อกำหนด "อินเทอร์เฟซ" นี้ แต่ไม่เป็นที่นิยมใน Ruby
มีไว้เพื่อการสาธิต
อย่างไรก็ตาม เรายังคงต้องการแบ่งปัน initialize
วิธีระหว่างรัฐทั้งหมดเพราะทั้งหมดต้องการบริบท (TrafficLight
วัตถุ) เพื่อส่งสัญญาณ การเปลี่ยนแปลงสถานะ .
ตอนนี้ :
ทั้ง 3 รัฐดูคล้ายกันมาก ฉันจะแสดงรหัสสำหรับสถานะใดสถานะหนึ่งให้คุณดู
นี่คือ Green
รัฐ:
class Green < State def beep puts "Color is now green" end def next_state @light.next_state(Yellow) end def start_timer sleep 5; next_state end end
ทุกรัฐรู้ดีว่าควรเปลี่ยนไปใช้รัฐต่อไปอย่างไรและเมื่อใด
ตัวอย่างเกม AI
คุณสามารถใช้เครื่องสถานะเพื่อแก้เกมที่ขึ้นอยู่กับสถานะปัจจุบัน เช่น RubyWarrior
ใน RubyWarrior คุณจะได้รับสิ่งของของผู้เล่น &กระดาน
เป้าหมายคือ :
- กำจัดศัตรูทั้งหมดบนกระดาน
- ไปถึงทางออกโดยรักษา HP ของคุณให้สูงกว่า 0
คุณสามารถขยับได้ทีละครั้ง &คุณต้องเลือกให้ดีหากต้องการผ่านด่าน
การดูสถานะปัจจุบันจะช่วยให้คุณตัดสินใจได้
นั่นเป็นเหตุผลที่เครื่องของรัฐเป็นทางออกที่ดี
นี่คือตัวอย่าง :
class Attacking < State def play(warrior) warrior.attack! @player.set_state(Healing) unless enemy_found?(warrior) end end
นี่เป็นหนึ่งในรัฐที่นักรบของเราสามารถเข้าไปได้ เมื่อเราไม่มีศัตรูอยู่ในสายตา เราจะย้ายเข้าไปอยู่ใน Healing
ให้ฟื้นจากความเสียหายจากการรบ
การใช้อัญมณี AASM
หากคุณต้องการติดตามสถานะปัจจุบันในขณะที่ทำให้แน่ใจว่าการเปลี่ยนภาพนั้นถูกต้อง คุณสามารถใช้เครื่องอัญมณีของรัฐ เช่น AASM
อัญมณีชิ้นนี้สร้างขึ้นจากแนวคิดเหตุการณ์ (เหมือนกดสวิตซ์ไฟ) ที่จะเปลี่ยนสถานะเป็นอย่างอื่น
นี่คือตัวอย่าง :
require 'aasm' class Light include AASM aasm do state :on, :off event :switch do transitions :from => :on, :to => :off, :if => :on? transitions :from => :off, :to => :on, :if => :off? end end end
วิธีใช้คลาสนี้ :
light = Light.new p light.on? # true light.switch p light.on? # false
การใช้เครื่องสถานะนี้ คุณสามารถเปลี่ยนเป็นสถานะ "เปิด" ได้ก็ต่อเมื่อสถานะปัจจุบันเป็น "ปิด" นอกจากนี้คุณยังสามารถมีการโทรกลับจำนวนหนึ่ง (ก่อน/หลัง) เพื่อเรียกใช้โค้ดเฉพาะระหว่างการเปลี่ยนสถานะ
การโทรกลับเหล่านี้ อาจรวมถึงสิ่งต่างๆ เช่น:
- ส่งอีเมล
- บันทึกการเปลี่ยนแปลงสถานะ
- กำลังอัปเดตแดชบอร์ดการตรวจสอบแบบสด
นอกจากนี้ AASM ยังมีตัวเลือกในการบันทึกสถานะปัจจุบันไปยังฐานข้อมูลโดยใช้ ActiveRecord
.
วิดีโอ AASM Gem
สรุป
คุณได้เรียนรู้เกี่ยวกับเครื่องจักรของรัฐ รูปแบบการออกแบบของรัฐ และอัญมณี AASM! เรียนรู้ต่อไปโดยสมัครรับจดหมายข่าว Ruby ของฉัน (สมาชิกมากกว่า 7000 คน) เพื่อให้คุณ ไม่พลาดบทความใหม่ &เคล็ดลับ Ruby เฉพาะสำหรับสมาชิก .
ตอนนี้ได้เวลาฝึกฝนแนวคิดใหม่ๆ เหล่านี้แล้ว 🙂
ขอบคุณสำหรับการอ่าน!