Máy trạng thái có thể chứa tất cả các trạng thái có thể có của thứ gì đó và các chuyển đổi được phép giữa các trạng thái này. Ví dụ:máy trạng thái cho một cánh cửa sẽ chỉ có hai trạng thái (open và closed ) và chỉ có hai lần chuyển đổi (opening và closing ).
Mặt khác, các máy trạng thái phức tạp có thể có nhiều trạng thái khác nhau với hàng trăm lần chuyển đổi giữa chúng. Trên thực tế, nếu nhìn xung quanh, bạn sẽ nhận thấy các máy trạng thái hữu hạn xung quanh mình — khi bạn mua hàng từ một trang web, nhận một gói khoai tây chiên giòn từ máy bán hàng tự động hoặc thậm chí chỉ cần rút tiền từ máy ATM.
Trong bài đăng này, chúng ta sẽ xem cách thiết lập máy trạng thái trong Ruby và sử dụng gem máy trạng thái.
Máy trạng thái đang được phát triển
Khi nào chúng ta cần một cỗ máy trạng thái đang được phát triển? Câu trả lời đơn giản là bất cứ khi nào bạn muốn lập mô hình nhiều quy tắc để chuyển đổi trạng thái hoặc thực hiện các tác dụng phụ trên một số chuyển đổi. Chìa khóa ở đây là xác định các phần trong ứng dụng của bạn sẽ được hưởng lợi từ máy trạng thái. Một ví dụ điển hình luôn phù hợp với tôi là Order trong bối cảnh của một ứng dụng thương mại điện tử.
Đối với một ứng dụng đơn giản bán sản phẩm trực tuyến, Order có thể ở một trong nhiều trạng thái:
createdprocessingreadyshippeddeliveredvoid
Bạn có thể xem các chuyển đổi được phép trong sơ đồ sau.

Hình dung đơn hàng dưới dạng máy trạng thái ngay lập tức cho phép chúng tôi hiểu rõ ràng toàn bộ quy trình, từ đặt hàng sản phẩm đến giao hàng. Và đối với các nhà phát triển như chúng tôi, nó cho phép làm rõ, đặt ra các bước về những gì có thể và không thể thực hiện tại các điểm cụ thể trong quy trình đó.
Nói như vậy, thật dễ dàng để nhảy xuống hố thỏ bằng cách chọn vấn đề này cho một vấn đề rất rộng và sau đó kết thúc với hàng trăm trạng thái khó lý giải và khó thực hiện. Vì vậy, hãy luôn có ý tưởng cấp cao nhất và biểu đồ trạng thái cho máy trạng thái được đề xuất của bạn trước khi triển khai nó.
Máy trạng thái đầu tiên của chúng tôi bằng Ruby
Hãy thử triển khai OrderStateMachine của chúng tôi với Ruby.
Đó là cách triển khai đủ đơn giản. Đối với mỗi lần chuyển đổi có thể có trong máy trạng thái, chúng tôi xác định một phương thức mới thực hiện một số kiểm tra độ chính xác và thực hiện chuyển đổi.
Điều tuyệt vời khi triển khai ở trên là mọi thứ đều rõ ràng:bất kỳ nhà phát triển mới nào cũng có thể nhanh chóng hiểu được toàn bộ phạm vi của máy trạng thái.
Hãy xem cách chúng tôi có thể thêm một số tác dụng phụ vào quá trình chuyển đổi. Chúng tôi sẽ tự động gửi các đơn hàng đã được đóng gói và có thể giao được:
Mặc dù cách triển khai đơn giản hoạt động hiệu quả nhưng lại quá dài dòng, đặc biệt khi chúng ta có nhiều chuyển đổi và điều kiện.
Kiểm tra nhanh Hộp công cụ Ruby sẽ mang lại một số viên ngọc cho máy trạng thái.state_machines và aasm là những cái phổ biến nhất và cả hai đều đi kèm với bộ chuyển đổi ActiveRecord nếu bạn muốn sử dụng chúng với Rails. Cả hai đều được kiểm tra kỹ lưỡng và sẵn sàng sản xuất, vì vậy hãy kiểm tra xem bạn có cần triển khai máy trạng thái hay không.
Sử dụng State Machines Gem trong Ruby
Đối với bài đăng này, tôi sẽ mô tả cách chúng ta có thể lập mô hình máy trạng thái cho Order của mình sử dụng state_machines đá quý.
Lớp trên định nghĩa máy trạng thái đơn giản của chúng ta và tất cả các chuyển đổi có thể có của nó. Trên hết, nó cũng tự động hiển thị rất nhiều phương thức tiện ích theo thứ tự:
Thực thi các tác dụng phụ với State Machines Gem trong Ruby
Như thường lệ đối với bất kỳ hệ thống thực tế nào, nhiều chuyển đổi trong máy trạng thái sẽ đi kèm với các tác dụng phụ.state_machines gem giúp bạn dễ dàng xác định và thực thi chúng. Hãy thêm hai tác dụng phụ:
- Trên tất cả các chuyển đổi sang
ready, tạo lô hàng giao hàng nếu đơn hàng làdeliverable. - Trên tất cả
fail_deliverysự kiện, hãy gửi email tới người dùng để thông báo về sự kiện đó.
Chúng ta có thể thực hiện chuyển tiếp như bình thường theo thứ tự:
Tôi khuyên bạn nên kiểm tra trang Github để tìm state_machines gem để tìm hiểu về tất cả các tùy chọn có thể có mà gem cung cấp.
Sử dụng State Machines Gem với ActiveRecord trong Ruby on Rails
Như đã thảo luận trước đó, cả state_machines và aasm hỗ trợ ActiveRecord. Khi bạn sử dụng state_machines gem bằng ActiveRecord, không có nhiều thứ thay đổi khi triển khai. Bạn có thể tiếp tục sử dụng hầu hết những gì chúng ta đã thảo luận trong các phần trước của bài đăng này.
Dưới đây là một số tính năng bổ sung mà bạn nhận được:
- Tác dụng phụ xảy ra bên trong một giao dịch. Điều này có nghĩa là nếu bạn thực hiện một số thao tác cơ sở dữ liệu bên trong hook chuyển đổi và giao dịch không thành công thì thay đổi trạng thái sẽ không được thực hiện. Xem
use_transactionsnếu bạn muốn tắt hành vi này. - Giống như tất cả các lệnh gọi lại ActiveRecord khác, nếu bạn muốn cập nhật các thuộc tính của mô hình hiện tại từ quá trình chuyển đổi, bạn phải thực hiện việc này bên trong
before_transitiongọi lại.Để cập nhật các thuộc tính bên trongafter_transition, bạn cần lưu lại mô hình. Hãy xem ví dụ sau:
- Đá quý tự động cung cấp phạm vi để lọc các mô hình theo trạng thái của chúng. Ví dụ:bạn có thể thực hiện
Order.with_state(:processing)để tìm tất cả các lệnh xử lý hoặcOrder.without_state(:processing)để tìm tất cả các đơn hàng chưa được xử lý. - Nếu cố gắng thay đổi trạng thái mà không có chuyển đổi phù hợp (ví dụ:từ
processingtớidelivered), bản ghi sẽ không lưu được và lỗi sẽ được thêm vào các lỗi xác thực. Để quốc tế hóa các thông báo lỗi được tạo, chỉ cần thêm một số khóa để cung cấp bản dịch cho các trạng thái và sự kiện bên trong tệp cấu hình (ví dụ:en.yml):
Ghi chú bên lề :Hãy xem bài đăng này để khắc phục sự cố về hiệu suất của ActiveRecord.
Kết thúc:Xây dựng máy trạng thái trong Ruby
Trong bài đăng này, chúng tôi đã khám phá lý do tại sao chúng tôi nên sử dụng máy trạng thái trong quá trình phát triển, trước khi xây dựng một máy trạng thái đơn giản. Cuối cùng, chúng tôi đã xem xét việc sử dụng gem máy trạng thái trong Ruby và Ruby on Rails.
Bây giờ bạn đã có hiểu biết cơ bản về máy trạng thái, tôi chắc chắn rằng bạn sẽ nhận ra một số tình huống xung quanh mình đã sử dụng máy trạng thái hoặc sẽ được hưởng lợi rất nhiều từ máy trạng thái.
Cho đến lần sau, tôi chúc bạn may mắn khi xây dựng các cỗ máy trạng thái!
Tái bút. Nếu bạn muốn đọc các bài đăng của Ruby Magic ngay khi chúng được đăng tải, hãy đăng ký nhận bản tin Ruby Magic của chúng tôi và không bao giờ bỏ lỡ một bài đăng nào!
Pulkit Goyal
Tác giả khách mời Pulkit của chúng tôi là kỹ sư và nhà tư vấn full-stack cấp cao. Khi rảnh rỗi, anh ấy viết về những trải nghiệm của mình trên blog.
Tất cả bài viết của Pulkit Goyal