Command: remote control và khe cắm lệnh
Định nghĩa Command, chiếc remote bảy khe, và cách tách bên bấm nút khỏi bên làm việc bằng đối tượng lệnh.
Trung bìnhDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học cách gói yêu cầu thành món đồ. Bạn tách nút bấm khỏi máy làm.
Kiến thức cần nhớ
Gói yêu cầu thành đối tượng
- Mỗi yêu cầu là 1 món lệnh riêng.
- Lệnh biết gọi đúng máy đúng việc.
- Nhờ đó lưu, xếp hàng, ghi nhật ký được.
- Ví dụ đời thường: phiếu gọi món trong quán.
Remote 7 khe và 4 vai
- Khe chỉ giữ 2 lệnh bật và tắt.
- Lệnh giữ máy thật bên trong.
- Máy chỉ lo làm, không biết remote.
- Ví dụ đời thường: ổ cắm không biết cắm quạt hay đèn.
Ba nhịp bấm nút
- Tay bấm nút ở khe.
- Khe gọi lệnh đang cắm.
- Lệnh gọi đúng máy của nó.
- Ví dụ đời thường: bấm công tắc, dây gọi bóng đèn.
| Cách làm | Thêm máy mới |
|---|---|
| Gọi thẳng máy | Mở remote ra sửa |
| Qua lệnh | Cắm thêm lệnh mới |
| Khe trống | Cắm lệnh rỗng cho êm |
Ví dụ
- Input: bấm bật ở khe đèn. Output: đèn sáng, máy khác im.
- Giải thích: khe chỉ gọi lệnh, lệnh gọi đúng đèn.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-028/.
Bài 1: Remote 7 khe
struct Command { virtual void execute() = 0; virtual std::string name() const = 0; virtual ~Command() = default; };
class RemoteControl {
public:
RemoteControl(); // 7 khe, mặc định cắm NoCommand
void setCommand(int slot, std::shared_ptr<Command> on, std::shared_ptr<Command> off);
void onButtonPressed(int slot);
void offButtonPressed(int slot);
std::string toString() const; // liệt kê 7 khe
};
| Máy (receiver) | Hàm |
|---|---|
Light(room) | on(), off(), isOn() |
Stereo | on(), off(), setCD(), setVolume(int) |
GarageDoor | up(), down(), isOpen() |
Viết lệnh: LightOn, LightOff, StereoOnWithCD (bật, chọn CD, âm lượng 11), StereoOff, GarageDoorUp, GarageDoorDown, NoCommand.
setCommand(0, LightOn(phòng khách), LightOff(phòng khách))
onButtonPressed(0) -> phòng khách sáng
onButtonPressed(6) -> khe trống, không làm gì, không crash
onButtonPressed(9) -> ném out_of_range
Ràng buộc: RemoteControl không #include bất kỳ receiver nào.
Bài 2: Lệnh bằng lambda và máy mới
- Viết
LambdaCommand(std::string name, std::function<void()> f). Cắm lệnh bật quạt mà không cần viết lớp riêng. - Thêm
CeilingFanvớihigh(),medium(),low(),off(). Thêm vào remote mà không sửaRemoteControl.
Bài 3: Hàng đợi lệnh và nhật ký (nâng cao)
CommandQueuenhận lệnh từ nhiều nơi,runAll()chạy theo thứ tự FIFO.LoggingCommandbọc một lệnh khác, ghi"[time] name"vào vector trước khi chạy.- Viết kịch bản: 5 lệnh được đưa vào hàng đợi, chạy hết, in nhật ký.
queue: LightOn, GarageDoorUp, StereoOnWithCD, LightOff, GarageDoorDown
log sau runAll -> 5 dòng đúng thứ tự
✅ Tiêu chí hoàn thành
- Test mẫu pass.
- Remote không phụ thuộc receiver cụ thể.
- Khe trống dùng
NoCommand, không có kiểm tranullptrrải rác. - Vẽ sơ đồ 4 vai: Client, Invoker, Command, Receiver.
❓ Câu hỏi ôn tập
- Command là gì, nói 1 câu?
- Remote gồm 4 vai nào?
- Vì sao khe không cần biết máy?
- Gói thành món đồ được lợi gì?
🌕 Bạn đã hoàn thành ngày 28. Làm hết bài tập phía trên rồi sang ngày tiếp theo.