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

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àmThêm máy mới
Gọi thẳng máyMở remote ra sửa
Qua lệnhCắm thêm lệnh mới
Khe trốngCắ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()
Stereoon(), off(), setCD(), setVolume(int)
GarageDoorup(), 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

  1. 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.
  2. Thêm CeilingFan với high(), medium(), low(), off(). Thêm vào remote mà không sửa RemoteControl.

Bài 3: Hàng đợi lệnh và nhật ký (nâng cao)

  1. CommandQueue nhận lệnh từ nhiều nơi, runAll() chạy theo thứ tự FIFO.
  2. LoggingCommand bọc một lệnh khác, ghi "[time] name" vào vector trước khi chạy.
  3. 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 tra nullptr rả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.

Bớt paste mù, hiểu sâu từng dòng mình viết.

180 Days Challenge. 180 ngày cai nghiện vibe code.