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.
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ệnDocumentStatemà 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 khaiDocumentState:DraftState- Khi gọiedit, in raEditing draft.... Khi gọiapprove, in raDraft approved. Moving to review.và chuyển tài liệu sangReviewState.ReviewState- Khi gọiedit, in raCannot edit during review.(không đổi trạng thái). Khi gọiapprove, in raReview complete. Publishing document.và chuyển sangPublishedState.PublishedState- Khi gọiedit, in raCannot edit published document.. Khi gọiapprove, in raDocument 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.Documentcần có:Một trường riêng lưu trạng thái
DocumentStatehiện tại, được khởi tạo thànhDraftStatetrong 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ứceditcủ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ứcapprovecủ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ùnggetClass().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àedithoặcapprove(ví dụ:edit,approve,edit,approve).Tạo một
Documentvà in trạng thái ban đầu theo định dạngStatus: [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: PublishedStateHã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
}
}
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
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online