Giới thiệu về Gem State Machine
Giới thiệu State Machine là gì? Nó là một thuật ngữ dược dùng để miêu tả kỹ thuật tương tác và chuyển đổi của đối tượng và các trạng thái (state) thông qua thuộc tính. Một đối tượng sẽ có hữu hạn các trạng thái, để chuyển từ trạng thái này sang trạng thái khác thì cần có những sự kiện trạng ...
Giới thiệu
- State Machine là gì? Nó là một thuật ngữ dược dùng để miêu tả kỹ thuật tương tác và chuyển đổi của đối tượng và các trạng thái (state) thông qua thuộc tính. Một đối tượng sẽ có hữu hạn các trạng thái, để chuyển từ trạng thái này sang trạng thái khác thì cần có những sự kiện trạng thái. Ví dụ: Cột đèn tín hiệu giao thông chẳng hạn chẳng hạn, có ba đèn với các trạng thái khác nhau (màu). Tại bất kỳ thời điểm nào tín hiệu đèn có thể đang ở một trạng thái red, green, yellow. Nó chỉ có thể chuyển đổi từ trạng thái này sang trạng thái khác. State Machine Diagram Với các sự kiện chuyển đổi trạng thái:
- red -> green (được phép đi)
- green -> yellow (chuẩn bị đi)
- yellow -> red (dừng lại) Một tín hiệu đèn giao thông không thể đi từ xanh sang đỏ, bỏ qua màu vàng, hoặc màu vàng trở lại màu xanh lá cây. (hữu hạn trạng thái của đối tượng).. => Các hành vi của đối tượng được thực hiện thông qua các trạng thái và sự chuyển đổi trạng thái của chúng.
- Tại sao nên sử dụng State Machine?
- State Machine giúp chúng ta định nghĩa các hành vi của một đối tượng một cách đơn giản và hiệu quả.
- Khi chuyển đổi trạng thái, khởi tạo trạng thái của đối tượng sẽ được kích hoạt tại mỗi sự kiện. Tại mỗi sự kiện đối tượng đều được xác định trạng thái cho phép thì mới được thực hiện.
- Dùng State Machine sẽ quản lý được toàn bộ các trạng thái và chuyển đổi trạng thái đối tượng chỉ bằng một machine duy nhất. => Vậy làm thế nào để sử dụng machine state? Có rất nhiều gem giúp chúng ta sử dụng state machine trong ruby. Chúng ta sẽ thực hành qua một ví dụ cụ thể trong rails với gem "state_machine" - một state machines trong ruby.
- Khi nào thì sử dụng State Machine?
- Khi thuộc tính của đối tượng có nhiều trạng thái khác nhau
- Với mỗi sự kiện chuyển đổi trạng thái của đối tượng có kèm theo các tiến trình theo sau đó. (bởi State machine còn hỗ trợ Transaction và các hàm callback)
Cài đặt
Thêm gem vào Gemfile của ứng dụng của bạn:
gem 'state_machine' gem 'ruby-graphviz', :require => 'graphviz'
Và sau đó thực hiện:
$$bundle
Hoặc tự cài đặt bằng:
$$gem install state_machines
Tạo Model
$$rails generate model name_model state:string rake db:migrate
Ví dụ: so sánh
Chúng ta sẽ tạo model cho một cuộc gọi cho ứng dụng điện thoại. Một cuộc gọi sẽ có trạng thái có trả lời hay không. Trước hết chúng ta sẽ sử dụng boolean answered để trả về kết quả trạng thái.
class Call attr_accessor :answered def initialize self.answered = false end end
Cách xử lý trường hợp không trả lời được hoặc cuộc gọi bị lỗi
class Call attr_reader :answered, :failed, :failure_reason def initialize @answered = false @failed = false end def answer @answered = true end def fail(reason) @failed = true @failure_reason = reason end end
Nhưng khi chúng ta muốn theo dõi xem cuộc gọi đang quay số hay đang trong cuộc nói chuyện hay muốn biết thời gian cuộc nói chuyện, .... Khi đó chúng cần định nghĩa thêm các phương thức với mỗi phương thức lại cần phải thêm các điều kiện để kiểm tra cho các boolean khác nhau. => Model Call sẽ trở lên rất phức tạp khi chúng ta thêm nhiều thuộc tính và trạng thái. Thay thế với State Machine Ở mỗi một thời điểm Call sẽ có trạng thái khác nhau:
- dialing
- in_progress
- completed
- failed Quy định sự thay đổi trạng thái:
- dialing -> in_progress or failed
- in_progress -> completed Phần xử lý với mỗi bối cảnh:
- failure_reason thông báo lỗi nếu cuộc gọi fail
- duration tính thời gian nếu cuộc gọi thành công Tất cả các hành vi của Call đều thể dễ dàng mô tả bằng cách sử dụng một state_machine thay vì quản lý thủ công nhiều booleans và flags:
# Using the state_machine gem. class Call state_machine :state, :initial => :dialing do event :answered do transition :dialing => :in_progress end event :failure do transition :dialing => :failed end event :hangup do transition :in_progress => :completed end state :completed do def duration @end_time - @start_time end end state :failed do attr_accessor :failure_reason end before_transition :dialing => :in_progress, :do => :rec_start_time after_transition :in_progress => :completed, :do => :rec_end_time end def answered? in_progress? || completed? end private def rec_start_time @start_time = Time.now end def rec_end_time @end_time = Time.now end end
**Note: cách sử dụng **
Để sử dụng được State Machine cho đối tượng bạn cần gọi state_machine
state_machine :state, :initial => :dialing do end
- state là thuộc tính của đối tượng
- state_machine định nghĩa giá trị default cho đối tượng khi khởi tạo
- Trong quá trình tạo dữ liệu nếu bạn muốn thay đổi giá trị default của state bạn cần gọi state_machine khác.
call = Call.new # => #<Call:0xb7cf4eac @state="dialing"> call.state # => "dialing"
- Khi chuyển đổi trạng thái của đối tượng bạn sẽ gọi đến event
- Tại event sẽ kiểm tra trạng thái và chuyển đổi trạng thái đối tượng đó
event :answered do transition :dialing => :in_progress end
//rails c call.dialing? # => true call.in_progress? # => false call.answered # => true call.state = 'in_progress'