OOP Bài 6: Trạng thái (State) và Hành vi (Behavior) tương tác ra sao?
1. Trạng thái (State) - Bức ảnh chụp tại thời điểm T
Trạng thái không phải là bản thân các thuộc tính, mà là giá trị cụ thể của tất cả các thuộc tính tại một thời điểm nhất định.
Khi chương trình đang chạy, thời gian trôi đi, giá trị của các thuộc tính biến đổi, kéo theo trạng thái của Object chuyển từ dạng này sang dạng khác (State Transition).
- Góc nhìn thực chiến: Trong hệ thống AFC, một vé tàu có thuộc tính
$status. Tại 8:00 sáng, trạng thái của nó làINACTIVE(chưa kích hoạt). Tại 8:05, sau khi khách mua tại quầy, trạng thái làACTIVE(sẵn sàng quẹt). Tại 18:00, khi vé hết hạn, trạng thái làEXPIRED.
2. Hành vi (Behavior) - Tác nhân gây biến đổi
H hành vi (các phương thức) chính là động cơ thúc đẩy sự thay đổi của Trạng thái, hoặc là ống kính để quan sát Trạng thái đó. Hành vi đóng hai vai trò cốt lõi trong một hệ thống sống:
- Behavior làm thay đổi State: Khách gọi hàm
topUp(50000), thuộc tính$balancethay đổi. - State quy định Behavior: Hàm
topUp()sẽ thất bại ngay lập tức nếu State hiện tại đang làBLOCKED. Trạng thái quyết định xem một hành vi có được phép thực thi hay không.
3. Tương tác hai chiều: Minh họa qua Cửa xoay (Turnstile)
Để thấy rõ sự ràng buộc qua lại giữa State và Behavior, hãy xem code mô phỏng một cửa xoay kiểm soát vé (Turnstile) tại ga tàu. Cửa này chỉ có 2 trạng thái: LOCKED (Khóa) và UNLOCKED (Mở).
class Turnstile {
// 1. STATE (Trạng thái hiện tại của cửa)
private $state;
public function __construct() {
$this->state = 'LOCKED'; // Trạng thái khởi thủy
}
// 2. BEHAVIOR: Đút vé / Quẹt thẻ
public function insertTicket() {
// State chặn Behavior: Nếu cửa đang mở, không nhận thêm vé
if ($this->state === 'UNLOCKED') {
echo "Lỗi: Cửa đã mở, xin mời bước qua. Không quẹt lại thẻ!\n";
return;
}
// Behavior thay đổi State: Mở khóa cửa
$this->state = 'UNLOCKED';
echo "Thẻ hợp lệ. Cửa đã mở (UNLOCKED)!\n";
}
// 3. BEHAVIOR: Đẩy cửa bước qua
public function pushBarrier() {
// State chặn Behavior: Cửa đang khóa mà cố đẩy -> Báo động
if ($this->state === 'LOCKED') {
echo "BÁO ĐỘNG: Có người cố tình vượt rào trái phép!\n";
return;
}
// Behavior thay đổi State: Khách qua xong, khóa cửa lại
$this->state = 'LOCKED';
echo "Khách đã đi qua. Cửa đóng (LOCKED)!\n";
}
}
// === CHẠY THỬ NGHIỆM ===
$gate = new Turnstile();
// Kịch bản 1: Cố tình đẩy khi chưa quẹt thẻ
$gate->pushBarrier(); // BÁO ĐỘNG! (Do State đang là LOCKED)
// Kịch bản 2: Quẹt thẻ đúng quy trình
$gate->insertTicket(); // Cửa mở, State -> UNLOCKED
// Kịch bản 3: Quẹt thẻ 2 lần liên tiếp
$gate->insertTicket(); // Báo lỗi: Cửa đã mở rồi
// Kịch bản 4: Bước qua
$gate->pushBarrier(); // Khách đi qua, State -> LOCKED
4. Bài toán thực tế ở hệ thống lớn (Concurrency)
Khi thiết kế backend, sự tương tác giữa State và Behavior có vẻ đơn giản nếu hệ thống chỉ chạy một luồng (Single-thread). Nhưng chuyện gì xảy ra nếu hai Hành vi cố gắng thay đổi cùng một Trạng thái tại cùng một phần nghìn giây?
Ví dụ: Hệ thống vé API xử lý hàng ngàn request mỗi giây. Request A gọi hàm trừ_tiền() cùng lúc Request B gọi hàm hủy_vé(). Nếu không quản lý kỹ Trạng thái nội bộ (bằng Row-level lock trong DB hoặc Mutex trong bộ nhớ), Object sẽ rơi vào một Trạng thái rác (Data Inconsistency) khiến tiền bị trừ mà vé vẫn bị hủy.
All rights reserved