Menu
CoddyTech

Mẫu State

Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 74 trên 87.

Mẫu State là một mẫu thiết kế hành vi cho phép đối tượng thay đổi cách hoạt động khi trạng thái bên trong thay đổi. Đối tượng có vẻ như thay đổi lớp, nhưng thực tế, nó ủy quyền hành vi cho các đối tượng trạng thái khác nhau. Hãy nghĩ đến máy bán hàng tự động: phản hồi của máy khi bạn nhấn nút phụ thuộc vào việc máy đang ở trạng thái chờ, đã nhận tiền hay đang giao sản phẩm.

Thay vì dùng các câu lệnh điều kiện phức tạp để xử lý các trạng thái khác nhau, bạn đóng gói mỗi trạng thái trong một lớp riêng. Mẫu này gồm một Context giữ tham chiếu đến trạng thái hiện tại và các lớp State xác định hành vi cho từng trạng thái:

interface State {
    void handle(Context context);
}

class Context {
    private State state;
    
    public void setState(State state) {
        this.state = state;
    }
    
    public void request() {
        state.handle(this);
    }
}

Mỗi trạng thái cụ thể triển khai hành vi và có thể kích hoạt chuyển đổi sang các trạng thái khác:

class IdleState implements State {
    public void handle(Context context) {
        System.out.println("Idle: Insert coin to start");
        context.setState(new ActiveState());
    }
}

class ActiveState implements State {
    public void handle(Context context) {
        System.out.println("Active: Processing...");
        context.setState(new IdleState());
    }
}

Ngữ cảnh chuyển tiếp các yêu cầu đến trạng thái hiện tại, và hành vi tự động thay đổi khi trạng thái thay đổi:

Context context = new Context();
context.setState(new IdleState());

context.request();  // Chờ: Bỏ xu vào để bắt đầu
context.request();  // Hoạt động: Đang xử lý...

Mẫu trạng thái (State Pattern) rất phù hợp khi hành vi của một đối tượng phụ thuộc nhiều vào trạng thái của nó và cần thay đổi trong thời gian chạy. Mẫu này loại bỏ các khối lệnh điều kiện lớn và giúp bạn dễ dàng thêm trạng thái mới: chỉ cần tạo một lớp mới triển khai giao diện State.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống quy trình xử lý tài liệu bằng mẫu State! Bạn sẽ tạo một hệ thống mô phỏng cách tài liệu chuyển qua các giai đoạn Draft, Review và Published, trong đó mỗi trạng thái quyết định những thao tác nào khả dụng và tài liệu sẽ hoạt động ra sao khi bạn thử chỉnh sửa hoặc phê duyệt.

Bạn sẽ sắp xếp code trong bốn tệp:

  • DocumentState.java: Định nghĩa giao diện DocumentState mà tất cả các trạng thái sẽ triển khai. Giao diện khai báo hai phương thức: edit(Document doc) để thử chỉnh sửa tài liệu và approve(Document doc) để thử phê duyệt hoặc chuyển tài liệu sang giai đoạn tiếp theo.
  • States.java: Tạo ba lớp trạng thái cụ thể triển khai DocumentState:

    DraftState - Khi gọi edit, in ra Editing draft.... Khi gọi approve, in ra Draft approved. Moving to review. và chuyển tài liệu sang ReviewState.

    ReviewState - Khi gọi edit, in ra Cannot edit during review. (không đổi trạng thái). Khi gọi approve, in ra Review complete. Publishing document. và chuyển sang PublishedState.

    PublishedState - Khi gọi edit, in ra Cannot edit published document.. Khi gọi approve, in ra Document already published.. Cả hai thao tác đều không làm thay đổi trạng thái.

  • Document.java: Tạo lớp ngữ cảnh quản lý trạng thái hiện tại. Document cần có:

    Một trường riêng lưu trạng thái DocumentState hiện tại, được khởi tạo thành DraftState trong hàm khởi tạo.

    Phương thức setState(DocumentState state) để thay đổi trạng thái hiện tại.

    Phương thức edit() chuyển tiếp lời gọi đến phương thức edit của trạng thái hiện tại.

    Phương thức approve() chuyển tiếp lời gọi đến phương thức approve của trạng thái hiện tại.

    Phương thức getStatus() trả về tên lớp đơn giản của trạng thái hiện tại (dùng getClass().getSimpleName()).

  • Main.java: Minh họa hệ thống trạng thái của bạn! Bạn sẽ nhận một đầu vào: chuỗi các thao tác, phân tách bằng dấu phẩy, trong đó mỗi thao tác là edit hoặc approve (ví dụ: edit,approve,edit,approve).

    Tạo một Document và in trạng thái ban đầu theo định dạng Status: [state]. Sau đó xử lý từng thao tác theo thứ tự, gọi phương thức tương ứng trên tài liệu. Sau mỗi thao tác, in trạng thái hiện tại.

Bạn sẽ nhận một đầu vào: chuỗi các thao tác được phân tách bằng dấu phẩy.

Ví dụ, với đầu vào edit,approve,approve, đầu ra sẽ là:

Status: DraftState
Editing draft...
Status: DraftState
Draft approved. Moving to review.
Status: ReviewState
Review complete. Publishing document.
Status: PublishedState

Hãy chú ý cách cùng một lời gọi approve() tạo ra kết quả khác nhau tùy thuộc vào trạng thái hiện tại của tài liệu. Đây chính là cốt lõi của mẫu State! Tài liệu không dùng các câu lệnh điều kiện để kiểm tra trạng thái. Thay vào đó, nó chuyển tiếp hành vi đến đối tượng trạng thái hiện tại, nơi xử lý thao tác phù hợp và kích hoạt các lần chuyển trạng thái khi cần.

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Tạo một Document mới
        
        // TODO: In trạng thái ban đầu theo định dạng "Status: [state]"
        
        // TODO: Tách đầu vào theo dấu phẩy để lấy từng hành động
        
        // TODO: Lặp qua từng hành động
        // Nếu hành động là "edit", gọi edit() trên document
        // Nếu hành động là "approve", gọi approve() trên document
        // Sau mỗi hành động, in trạng thái hiện tại
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Java online