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
Ôn lại trước khi học
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.
| Pattern | Nỗi đau | Cách giải |
|---|---|---|
| Mediator | Object nối nhau chằng chịt | Nói qua 1 trạm |
| Memento | Muốn undo mà lộ nội tạng | Chụp vé kín |
| Prototype | Tạo mới tốn kém | Clone 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áp | Chi tiết |
|---|---|
| 1 đường băng | Chỉ 1 máy dùng tại một thời điểm |
| Hạ cánh ưu tiên hơn cất cánh | Hàng chờ riêng |
| Đường băng trống | Thá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)
Shapecóvirtual std::unique_ptr<Shape> clone() const = 0. ViếtCircle,Rectangle,Group(chứavector<unique_ptr<Shape>>).Group::clone()phải clone sâu. Viết test: sửa bản clone không đổi bản gốc.- Viết
ShapeRegistry: đăng ký mẫu theo tên,create(name)trả bản clone. - Cố tình viết bản clone nông cho
Group(dùngshared_ptrchia 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.
-
Historykhông truy cập được ruộtMemento(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.