Giới thiệu về Gem State Machine
Bài đăng này đã không được cập nhật trong 3 năm
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
orfailed
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 failduration
tính thời gian nếu cuộc gọi thành công Tất cả các hành vi củaCall
đề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ượngstate_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'
Tài liệu tham khảo
https://github.com/pluginaweek/state_machine https://github.com/state-machines/state_machines http://www.rubydoc.info/github/pluginaweek/state_machine/StateMachine/AllMatcher
All rights reserved