State Pattern – cho phép một đối tượng thay đổi hành vi của nó khi trạng thái nội bộ của nó thay đổi.
Trong thực tế một đối tượng khi đang ở trạng thái khác nhau thì sẽ có hành vi khác nhau. Ví dụ một đơn hành khi được khởi tạo sẽ ở Created, sau đó là Paying để tiến hành thanh toán, và Delivery để tiến hành vận chuyển.
Vấn đề đặt ra
Thông thường trong hầu hết trường hợp chúng ta sẽ thấy mọi hành động sẽ xoanh quanh một đối tượng củ thể và trạng thái của nó. Hãy lấy việc quản lý đơn hàng shoppe của bạn làm ví dụ, có thể dễ dàng tưởng tượng ra mọi hành động của bạn sẽ hoàn toàn phụ thuộc váo đối tượng DonHang
- Khi bạn mới order xong DonHang sẽ được hệ thống lưu và để ở trạng thái Created ở trạng thái này việc của bạn sẽ là quản lý danh sách sản phẩm, tính toán tiền vouncher …
- Tiếp theo sau khi khởi tạo thành công chúng ta cần đẩy nó sang Payment ở đây chúng ta sẽ xử lý việc thanh toán cho người dùng dùng thẻ hay tiền mặt, xử lý ngân hàng …
Bạn có thể sử dụng switch case không? Câu trả lời là nó không sai bạn vẫn có thể quản lý các state và action thực hiện ở state đó, tuy nhiên nếu làm như vậy bạn đã vi phạm nguyên tắt Open/Close, và mỗi khi có trạng thái mới function xử lý switch/case ngày càng nhiều hơn và kéo theo việc phải sửa unit test liên tục cũng như phải test lại toàn bộ.
Cài đặt State Pattern
Các thành phần tham gia State Pattern:
- Context: Là lớp có nhiều trạng thái, hành vi lớp sẽ bị thay đổi bởi trạng thái. Client không truy cập trực tiếp đến State của đối tượng. Lớp Context này chứa thông tin của ConcreteState object, cho hành vi nào tương ứng với trạng thái nào hiện đang được thực hiện.
- State: là một interface hoặc abstract class xác định các đặc tính cơ bản của tất cả các đối tượng ConcreteState. Chúng sẽ được sử dụng bởi đối tượng Context để truy cập chức năng có thể thay đổi.
- ConcreteState: cài đặt các phương thức của State. Mỗi ConcreteState có thể thực hiện logic và hành vi của riêng nó tùy thuộc vào Context.
Code implement
public class OrderContext {
private State state;
public void setState(State state) {
this.state = state;
}
public void applyState() {
this.state.handleRequest();
}
}
public interface State {
void handleRequest();
}
public class NewState implements State {
@Override
public void handleRequest() {
System.out.println("Create a new order");
}
}
public class PaymentState implements State {
@Override
public void handleRequest() {
System.out.println("Payment");
}
}
public static void main(String[] args) {
DocumentContext context = new DocumentContext();
context.setState(new NewState());
context.applyState();
context.setState(new PaymentState ());
context.applyState();
}
Ưu & nhược điểm
Ưu điểm
- Single Responsibility (SRP): Mỗi state sẽ chỉ thực hiện logic của riêng nó
- Đảm bảo nguyên tắc Open/Closed Principle (OCP): việc thêm một state không gây ảnh hưởng.
- Giữ hành vi cụ thể tương ứng với mỗi State.
- Giúp chuyển State một cách rõ ràng.
- Loại bỏ các câu lệnh xét trường hợp (If, Switch case) giúp đơn giản code của context
Nhược điểm
- Việc sử dụng state pattern có thể quá mức cần thiết nếu state machine chỉ có một vài trạng thái hoặc hiếm khi thay đổi có thể dẫn đến việc tăng độ phức tạp của code
- Sẽ xuất hiện các state trung gian gây khó khăn cho debug và rollback transaction.
Link tham khảo
https://viblo.asia/p/state-design-pattern-tro-thu-dac-luc-cua-developers-3P0lPB9PKox