Tìm hiểu về State Design Pattern trong Java

Tram Ho

Giới thiệu

Trong thực tế, có nhiều bài toán liên quan đến các trạng thái, ví dụ như trạng thái đơn hàng, trạng thái về playing music, trạng thái của content qua các quy trình xử lý … Trong những trường hợp như thế này, không thể không nhắc tới State Design Pattern, mẫu thiết kế này sẽ giúp chúng ta thiết kế cấu trúc, xử lý các logic liên quan đến trạng thái một cách dễ dàng và chuyên nghiệp hơn. Hôm nay chúng ta sẽ cùng tìm hiểu về mẫu thiết kế State (State Design Pattern), cụ thể cách hoạt động, cách implement mẫu thiết kế này trong Java.
Đầu tiên, chúng ta sẽ đưa ra một cái nhìn tổng quan về mục đích của nó và giải thích vấn đề mà nó cố gắng giải quyết. Sau đó, chúng ta sẽ cùng tìm kiểu về biểu đồ UML của nó và triển khai ví dụ thực tế.

Tổng quan về State Design Pattern

Ý tưởng chính của mẫu thiết kế State là nó cho phép các đối tượng có thể thay đổi hành vi, trạng thái của nó mà không thay đổi class của nó. Ngoài ra, bằng cách thực hiện nó, code sẽ gọn gàng và đẹp hơn mà không cần nhiều câu lệnh if/else.
Hãy tưởng tượng chúng ta có một gói hàng (package) được gửi đến bưu điện, chính gói đó có thể được đặt hàng (order), sau đó được gửi đến bưu điện và cuối cùng được khách hàng nhận. Bây giờ, tùy thuộc vào trạng thái thực tế, chúng ta muốn in trạng thái giao hàng của nó.
Cách tiếp cận đơn giản nhất là thêm một số flag boolean và áp dụng các câu lệnh if / other đơn giản trong mỗi phương thức của chúng ta trong lớp. Điều đó sẽ không làm phức tạp nó nhiều trong một kịch bản đơn giản. Tuy nhiên, nó có thể làm phức tạp và gây khó hiểu trong code của chúng ta khi chúng ta sẽ xử lý nhiều trạng thái hơn, điều này sẽ dẫn đến nhiều câu lệnh if / other hơn.
Ngoài ra, tất cả logic cho mỗi trạng thái sẽ được trải đều trên tất cả các phương thức. Bây giờ, đây là nơi mà mẫu thiết kế State có thể được xem xét sử dụng. Nhờ mẫu thiết kế State, chúng ta có thể gói gọn logic trong các lớp chuyên dụng, áp dụng Nguyên tắc Single Responsibility Principle và Nguyên tắc Mở / Đóng (Open–closed principle), code sẽ sạch hơn và dễ bảo trì hơn.

Biểu đồ UML

Hãy cùng nhìn qua biểu đồ UML cho mẫu thiết kế này:

Trong sơ đồ UML, chúng ta thấy rằng lớp Context có Trạng thái (State) liên kết sẽ thay đổi trong khi thực hiện chương trình.
Context của chúng ta sẽ ủy nhiệm hành vi thi hành trạng thái. Nói cách khác, tất cả các hành vi sẽ được xử lý bằng cách thực hiện cụ thể (handle method) của trạng thái .
Chúng ta thấy rằng logic được tách ra và việc thêm các trạng thái mới là đơn giản – cũng có thể thêm vào việc thực hiện Trạng thái khác nếu cần.

Implementation

Bây giờ hãy cùng tìm hiểu cách thực thi mẫu thiết kế này, như đã nói ở trên đối tượng của chúng ta là gói hàng (package), có các trạng thái như sau: đặt hàng (order), giao hàng (delivered) và nhận hàng (received). Vì thế chúng ta sẽ có 3 trạng thái cho lớp context (ở đây là package)
Đầu tiên, định nghĩa context, trong ví dụ cụ thể này nó chính là gói hàng (Package class):

Giái thích một chút về lớp này, Package class của chúng ta sẽ chứa một đối tượng state (nó chính là PackageState), PackageState sẽ là 1 interface đại diện cho 3 trạng thái đã nói ở trên: OrderedState, DeliveredStateReceivedState. Lớp Package thực thi 3 phương thức liên quan đến trạng thái: previousState (chuyển về trạng thái trước đó), nextState (chuyển sang trạng thái tiếp theo) và printStatus (thông báo trạng thái hiện tại của gói hàng), bắt đầu sẽ từ trạng thái OrderedState.
Tiếp theo, chúng ta sẽ có PackageState interface có ba phương thức như sau:

Interface này sẽ được implement bới 3 trạng thái tương ứng với 3 class: OrderedState, DeliveredStateReceivedState:
OrderedState class:

OrderedState là trạng thái đầu tiên của gói hàng, nó không thể chuyển về trạng thái trước đó, nhưng có thể chuyển sang trạng thái tiếp theo đó là DeliveredState
DeliveredState class:

DeliveredState là trạng thái thứ 2 của gói hàng trong ví dụ trên, nó có thể chuyển về trạng thái trước đó OrderedState, và cũng có thể chuyển sang trạng thái tiếp theo ReceivedState.
ReceivedState class:

ReceivedState là trạng thái cuối cùng của gói hàng, nó chỉ có thể chuyển về trạng thái trước đó DeliveredState.

Testing

Chúng ta hãy xem cách thực hiện. Trước tiên, hãy xác minh xem các chuyển tiếp thiết lập có hoạt động như mong đợi không bằng một vài đoạn test nhỏ:

Tiếp theo, kiểm tra nhanh xem gói hàng của chúng ta có thể quay lại trạng thái không:

Sau đó, hãy xác minh thay đổi trạng thái và xem cách triển khai phương thức printStatus() thay đổi cách thực hiện của nó khi chạy:

Và đây là output:

Hạn chế

Hạn chế mẫu trạng thái là việc thực hiện chuyển đổi giữa các trạng thái. Điều đó làm cho trạng thái hardcoded, đó là một thực tế xấu nói chung.
Tuy nhiên, tùy thuộc vào nhu cầu và yêu cầu của chúng ta, điều đó có thể hoặc không thể là một vấn đề.

Kết luận

Mẫu thiết kế State là tuyệt vời khi chúng ta muốn tránh các câu lệnh if/else thông thường. Thay vào đó, chúng ta trích xuất logic để phân tách các lớp và để đối tượng context của chúng ta ủy thác hành vi cho các phương thức được triển khai trong lớp trạng thái. Bên cạnh đó, chúng ta có thể tận dụng sự chuyển đổi giữa các trạng thái, trong đó một trạng thái có thể thay đổi trạng thái của context.

Tham khảo

State Design Pattern in Java – Baeldung

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo