Mẫu trạng thái
Thuộc phần Lập trình hướng đối tượng trong hành trình PHP của Coddy. Bài học 79 trên 91.
Mẫu trạng thái là một mẫu thiết kế hành vi cho phép một đối tượng thay đổi hành vi khi trạng thái bên trong thay đổi. Thay vì dùng các câu lệnh điều kiện phức tạp để xử lý những trạng thái khác nhau, bạn đóng gói mỗi trạng thái trong lớp riêng và giao việc xử lý cho đối tượng trạng thái hiện tại.
Hãy nghĩ đến một máy bán hàng tự động. Máy hoạt động khác nhau tùy theo việc máy đang chờ tiền, đã nhận thanh toán hay đang giao sản phẩm.
Mỗi trạng thái có quy tắc riêng về những hành động được phép. Mẫu State biểu diễn điều này bằng cách tạo một lớp cho mỗi trạng thái.
<?php
interface OrderState {
public function proceed(Order $order): string;
public function getStatus(): string;
}
class PendingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ProcessingState());
return "Order moved to processing";
}
public function getStatus(): string {
return "Pending";
}
}
class ProcessingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ShippedState());
return "Order has been shipped";
}
public function getStatus(): string {
return "Processing";
}
}
class ShippedState implements OrderState {
public function proceed(Order $order): string {
return "Order already shipped";
}
public function getStatus(): string {
return "Shipped";
}
}
Lớp ngữ cảnh lưu tham chiếu đến trạng thái hiện tại và chuyển các lệnh gọi đến trạng thái đó:
<?php
class Order {
private OrderState $state;
public function __construct() {
$this->state = new PendingState();
}
public function setState(OrderState $state): void {
$this->state = $state;
}
public function proceed(): string {
return $this->state->proceed($this);
}
public function getStatus(): string {
return $this->state->getStatus();
}
}
$order = new Order();
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus();
Đầu ra:
Pending
Order moved to processing
Processing
Order has been shipped
ShippedMẫu trạng thái giúp loại bỏ các câu lệnh switch cồng kềnh và giúp bạn dễ dàng thêm trạng thái mới. Mỗi lớp trạng thái chịu trách nhiệm xác định trạng thái tiếp theo, nhờ đó logic luôn gọn gàng và dễ bảo trì.
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. Tài liệu trong hệ thống quản lý nội dung trải qua nhiều giai đoạn: bản nháp, đang xét duyệt và đã xuất bản. Mỗi giai đoạn có quy tắc riêng về những hành động được phép và điều gì xảy ra khi bạn thử chuyển tài liệu sang giai đoạn tiếp theo.
Bạn sẽ sắp xếp code trong bốn tệp:
DocumentState.php: Định nghĩa một giao diệnDocumentStatelàm quy ước chung cho mọi trạng thái tài liệu. Giao diện này cần có hai phương thức:publish(Document $document): stringđể thử chuyển tài liệu sang giai đoạn tiếp theo vàgetStatus(): stringđể trả về tên trạng thái hiện tại.States.php: Bao gồm giao diện DocumentState và tạo ba lớp trạng thái triển khai giao diện đó:DraftState: Khi gọipublish(), phương thức chuyển tài liệu sang trạng tháiReviewStatevà trả về"Document sent for review". Trạng thái của nó là"Draft".ReviewState: Khi gọipublish(), phương thức chuyển tài liệu sang trạng tháiPublishedStatevà trả về"Document published". Trạng thái của nó là"Under Review".PublishedState: Khi gọipublish(), phương thức trả về"Document is already published"mà không thay đổi trạng thái. Trạng thái của nó là"Published".
Document.php: Bao gồm tệp States và tạo lớpDocumentđóng vai trò làm ngữ cảnh. Tài liệu cần:- Nhận tiêu đề trong hàm khởi tạo và bắt đầu ở trạng thái
DraftState - Có phương thức
setState(DocumentState $state): voidđể thay đổi trạng thái hiện tại - Có phương thức
publish(): stringủy quyền cho phương thứcpublish()của trạng thái hiện tại - Có phương thức
getStatus(): stringtrả về trạng thái hiện tại
- Nhận tiêu đề trong hàm khởi tạo và bắt đầu ở trạng thái
main.php: Bao gồm tệp Document. Bạn sẽ nhận một đầu vào: số lần gọipublish()trên tài liệu.Tạo một
Documentcó tiêu đề"My Article". Trước tiên, in ra trạng thái ban đầu. Sau đó, gọipublish()số lần đã chỉ định và in kết quả của mỗi lần gọi trên một dòng mới. Cuối cùng, in ra trạng thái cuối cùng.Định dạng đầu ra:
Status: [initial status] [publish result 1] [publish result 2] ... Status: [final status]
Mỗi lớp trạng thái biết trạng thái tiếp theo trong quy trình, giúp sắp xếp logic chuyển trạng thái rõ ràng và dễ chỉnh sửa. Để thêm một trạng thái mới như "Archived", bạn chỉ cần tạo một lớp mới và cập nhật các bước chuyển trạng thái liên quan.
Tự làm thử
<?php
require_once 'Document.php';
// Đọc đầu vào: số lần gọi publish()
$n = intval(trim(fgets(STDIN)));
// TODO: Tạo một Document với tiêu đề "My Article"
// TODO: In ra trạng thái ban đầu theo định dạng: "Status: [status]"
// TODO: Gọi publish() n lần, in mỗi kết quả trên một dòng mới
// TODO: In ra trạng thái cuối cùng theo định dạng: "Status: [status]"
?>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
1Nền tảng về OOP
Tệp bên ngoàiGiới thiệu về OOPLớp và đối tượngTừ khóa $thisPhương thứcThuộc tínhHàm khởi tạo (__construct)Hàm hủy (__destruct)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnTừ khóa parent::Ghi đè phương thứcTừ khóa finalLớp trừu tượngÔn tập: hệ thống phân cấp nhân viên7Đóng gói
Công khai, được bảo vệ, riêng tưTìm hiểu sâu về bộ điều chỉnh quyền truy cậpGetter và setterẨn thông tinRút gọn hàm khởi tạo (8.0)Ôn tập: hệ thống hồ sơ học sinh10Các khái niệm OOP nâng cao
Kết hợp hay kế thừaTiêm phụ thuộcLớp ẩn danhKiểu liệt kê (PHP 8.1)Fiber (PHP 8.1)Tìm hiểu sâu về sao chép đối tượngBộ sinh và bộ lặp13Mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpMẫu Repository2Không gian tên và tự động nạp
Giới thiệu về không gian tênTừ khóa useChuẩn tự động nạp PSR-4Trình tự động nạp của ComposerÔn tập: dự án có tổ chức5Giao diện và hợp đồng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnGiao diện và lớp trừu tượngGợi ý kiểu với giao diệnÔn tập: Máy tính hình học8Phương thức ma thuật
Giới thiệu về phương thức ma thuật__toString và __debugInfo__get, __set, __isset, __unset__call và __callStatic__clone và sao chép đối tượng__serialize và __unserializeÔn tập: Bộ sưu tập tùy chỉnh3Thuộc tính của lớp
Thuộc tính instance và staticHằng số trong lớpPhương thức và thuộc tính staticThuộc tính private và protectedThuộc tính chỉ đọc (PHP 8.1)Ôn tập: Trình quản lý tài khoản ngân hàng6Đa hình
Xem lại việc ghi đè phương thứcĐa hình qua giao diệnGợi ý kiểu và kiểu hợpLiên kết tĩnh muộnÔn tập: bộ xử lý thanh toánTự luyện tập: Trình biên dịch PHP online