MVC: compound pattern điều khiển nhịp DJ

Mô hình khung nhìn điều khiển qua ứng dụng nhịp DJ, cách ba tầng nói chuyện và compound pattern là gì.
KhóDesign pattern ~60 phút

Giới thiệu

Bài này học tách dữ liệu, hiển thị và điều khiển riêng. Dùng để thêm màn hình mới không sửa ruột cũ.

Kiến thức cần nhớ

Compound và 3 tầng

  • Định nghĩa: compound là cụm pattern cùng giải 1 bài.
  • Model giữ nhịp nhạc và luật đổi nhịp.
  • View chỉ hiển thị số và đèn chớp theo nhịp.
  • Ví dụ đời thường: như DJ, loa, nút vặn riêng nhau.
  • Controller nhận nút bấm rồi gọi model đổi.

Luồng người dùng

  • DJ bấm tăng nhịp trên controller.
  • Controller gọi model đổi nhịp mới.
  • Model báo cho mọi view đang nghe.
  • Ví dụ đời thường: như vặn nút rồi loa đổi theo.
  • Thêm view mới chỉ cần đăng ký nghe model.
TầngViệc duy nhấtKhông làm việc
ModelGiữ nhịp và luậtKhông vẽ màn hình
ViewVẽ theo số mớiKhông tự đổi số
ControllerNhận nút bấmKhông giữ dữ liệu

Luồng nhịp đập

  • Model tự sinh nhịp đều đặn theo giờ.
  • Mỗi nhịp báo cho các view đang nghe.
  • View đèn chớp 1 cái, view số giữ nguyên.
  • Ví dụ đời thường: như metronome gõ đều cho band.
  • Model không biết có mấy view đang nghe.

MVC trên web

  • Ứng dụng web chia MVC qua request: trình duyệt gửi request tới controller, controller gọi model, rồi chọn view để dựng trang trả về.
  • Khác MVC trên máy: view thường không đăng ký nghe model trực tiếp (Observer), mà nhận dữ liệu mỗi lần controller dựng trang.

Ví dụ

  • Input: bấm tăng từ 100 lên 120 -> Output: số và đèn cùng đổi.
  • Giải thích: model đổi 1 lần, mọi view tự vẽ lại.
# Controller gọi model, model báo view
model.set_nhip(120)  # đổi dữ liệu
for v in views:  # báo từng view
    v.ve_lai(model.nhip)

💻 Bài tập

Ngôn ngữ: C++17 (hoặc Java). Giao diện dạng console, cập nhật bằng in lại dòng. Nộp vào day-107/.

Bài 1: Model nhịp DJ

struct BeatObserver { virtual void updateBeat() = 0; virtual ~BeatObserver() = default; };
struct BPMObserver  { virtual void updateBPM() = 0;  virtual ~BPMObserver() = default; };
class BeatModel {
 public:
  void on(); void off();
  void setBPM(int bpm);     // kẹp trong [40, 240]
  int  getBPM() const;
  void tick();              // gọi từ vòng lặp thời gian, báo mọi BeatObserver
  void registerObserver(BeatObserver*); void registerObserver(BPMObserver*);
  void removeObserver(BeatObserver*);   void removeObserver(BPMObserver*);
};
setBPM(120) -> mọi BPMObserver nhận updateBPM, getBPM = 120
setBPM(500) -> getBPM = 240
off()       -> BPM = 0, tick không phát

Bài 2: View và Controller

Thành phầnViệc phải làm
DJViewĐăng ký cả 2 loại observer. In "BPM: 120" và 1 ký tự * mỗi nhịp
BeatControllerNhận lệnh start, stop, +, -, set N từ bàn phím. Gọi model. Bật tắt nút trên view (in trạng thái nút)
StrategyView chỉ gọi controller, controller có thể thay bằng lớp khác
start; + ; + ; + ; set 90; stop
-> BPM: 60 -> 61 -> 62 -> 63 -> 90 -> 0, nút Start/Stop đổi trạng thái đúng

Thêm BPMGraphView in thanh # dài theo BPM. Không sửa BeatModel.

Bài 3: Tái dùng view với model khác (nâng cao)

  1. Viết HeartModel có interface khác (getHeartRate(), observer riêng).
  2. Viết HeartAdapter để DJView dùng được HeartModel.
  3. Viết HeartController (không cho đổi nhịp tim: lệnh +, - bị bỏ qua, in cảnh báo).
  4. Liệt kê trong README.md: MVC này dùng những pattern nào, ở đâu (Observer, Strategy, Composite nếu có, Adapter).

✅ Tiêu chí hoàn thành

  • Test model pass (kẹp BPM, bật tắt, observer).
  • Thêm view mới không sửa model.
  • DJView chạy với HeartModel qua adapter.
  • Bảng pattern trong MVC chỉ đúng vị trí trong code.

❓ Câu hỏi ôn tập

  • Model giữ cái gì?
  • View có được đổi số không?
  • Ai nhận nút bấm?
  • Thêm view có sửa model không?

🌕 Bạn đã hoàn thành ngày 107. 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.