Mediator, Memento và Prototype

Trạm điều phối máy bay, vé lưu trạng thái game, và bản sao mẫu thay vì tạo mới trong ba pattern bổ sung.
KhóDesign pattern ~60 phút

Giới thiệu

Bài này học trạm điều phối, vé lưu game và nhân bản mẫu. Dùng để giảm dây nối chằng chịt giữa các object.

Kiến thức cần nhớ

Mediator

  • Định nghĩa: một trạm gom mọi giao tiếp giữa các object.
  • Các object chỉ biết trạm, không biết nhau.
  • Ví dụ đời thường: như tháp không lưu điều phối máy bay.
  • Thêm máy mới chỉ cần đăng ký vào trạm.
  • Tránh số đường nối tăng theo bình phương.

Memento

  • Định nghĩa: chụp trạng thái để khôi phục sau mà không lộ ruột.
  • Gồm 3 vai: nhân vật tự chụp, vé nhớ kín, menu giữ vé.
  • Ví dụ đời thường: như vé save game cất két không cần đọc.
  • Menu chỉ cất vé, không được mở xem bên trong.
  • Muốn quay lại thì lấy vé đưa nhân vật tự hồi.
PatternNỗi đauCách giải
MediatorObject nối nhau chằng chịtNói qua 1 trạm
MementoMuốn undo mà lộ nội tạngChụp vé kín
PrototypeTạo mới tốn kémClone từ mẫu

Prototype

  • Định nghĩa: tạo object mới bằng cách clone mẫu có sẵn.
  • Nhanh hơn dựng từ đầu khi khởi tạo nặng.
  • Ví dụ đời thường: như photo đơn mẫu thay vì viết lại.
  • Clone sâu thì độc lập, clone nông thì chia sẻ ruột.
  • Cần chú ý sao chép đúng phần thay đổi được.

Ví dụ

  • Input: 10 máy bay cần phối hợp -> Output: mỗi máy chỉ nối 1 trạm.
  • Giải thích: không còn đường nối tay đôi bình phương nữa.
# Clone mẫu thay vì dựng mới
import copy
quai_moi = copy.deepcopy(quai_mau)  # sao chép độc lập
print(quai_moi)  # dùng ngay

💻 Bài tập

Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-095/.

Bài 1: Mediator cho tháp điều khiển

struct Mediator { virtual void notify(Aircraft &sender, const std::string &event) = 0; virtual ~Mediator() = default; };
class ControlTower : public Mediator { /* giữ danh sách máy bay và trạng thái đường băng */ };
class Aircraft {
 public:
  Aircraft(std::string callsign, Mediator &m);
  void requestLanding(); void landed(); void requestTakeoff(); void tookOff();
  void receive(const std::string &msg);   // lưu tin nhắn nhận được để test
};
Luật của thápChi tiết
1 đường băngChỉ 1 máy dùng tại một thời điểm
Hạ cánh ưu tiên hơn cất cánhHàng chờ riêng
Đường băng trốngTháp báo máy kế tiếp trong hàng
A xin hạ cánh -> A nhận "được hạ cánh"
B xin cất cánh -> B nhận "chờ"
C xin hạ cánh -> C nhận "chờ"
A báo đã hạ cánh -> C (ưu tiên hạ cánh) nhận "được hạ cánh", B vẫn chờ

Ràng buộc: Aircraft không giữ tham chiếu tới Aircraft khác.

Bài 2: Memento cho trình soạn thảo

class Editor {
 public:
  void type(const std::string &s); void moveCursor(int pos); void select(int from, int to);
  class Memento { friend class Editor; /* private: text, cursor, selection */ };
  Memento save() const; void restore(const Memento &m);
};
class History { public: void push(Editor::Memento m); std::optional<Editor::Memento> pop(); };

Ràng buộc: History không đọc được nội dung Memento (không có getter public).

type "Hello"; save; type " World"; save; type "!"
restore 2 lần -> "Hello", con trỏ đúng vị trí lúc lưu

Bài 3: Prototype và clone sâu (nâng cao)

  1. Shape có virtual std::unique_ptr<Shape> clone() const = 0. Viết Circle, Rectangle, Group (chứa vector<unique_ptr<Shape>>).
  2. Group::clone() phải clone sâu. Viết test: sửa bản clone không đổi bản gốc.
  3. Viết ShapeRegistry: đăng ký mẫu theo tên, create(name) trả bản clone.
  4. Cố tình viết bản clone nông cho Group (dùng shared_ptr chia sẻ con). Viết test cho thấy lỗi.

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

  • Test tháp điều khiển pass đúng thứ tự ưu tiên.
  • History không truy cập được ruột Memento (kiểm bằng biên dịch).
  • Clone sâu độc lập, có test chứng minh clone nông sai.
  • Viết 1 câu cho mỗi pattern: vấn đề nó giải quyết.

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

  • Mediator giảm dây nối thế nào?
  • 3 vai trong Memento là gì?
  • Khi nào nên clone thay vì new?
  • Clone nông khác clone sâu ra sao?

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