Composite: thực đơn cây, trong suốt vs an toàn
Định nghĩa Composite, thực đơn lồng thực đơn con, và đánh đổi giữa trong suốt dễ dùng với an toàn đúng kiểu.
KhóDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học Composite để xử lý cây đồng nhất. Bạn dùng nó cho thực đơn lồng nhau nhiều tầng.
Kiến thức cần nhớ
Định nghĩa Composite
- Gộp đối tượng thành cây để biểu diễn phân cấp phần tử.
- Khách hàng đối xử cụm và lá như nhau.
- Từ khóa là cây và đồng nhất.
- Ví dụ đời thường: như thư mục chứa file và thư mục con, mở đều như nhau.
Thực đơn cây
- Thực đơn tổng chứa thực đơn con, thực đơn con chứa món lá.
- Cụm giữ danh sách con, gọi in thì gọi lan xuống từng con.
- Lá chỉ in chính nó, không có con.
- Ví dụ đời thường: như gọi cả bàn ăn, bếp tự lo từng món.
Trong suốt vs an toàn
- Trong suốt: thêm bớt nằm ở giao diện chung, khách không rẽ nhánh.
- An toàn: thêm bớt chỉ ở cụm, lá gọn sạch.
- Trong suốt lỗi phát hiện lúc chạy, an toàn lỗi phát hiện lúc biên dịch.
- Bài này chọn trong suốt vì đơn giản cho khách.
| Khía cạnh | Trong suốt | An toàn |
|---|---|---|
| Giao diện chung | Có thêm bớt | Chỉ việc chung |
| Khách hàng | Không rẽ nhánh | Phải kiểm tra cụm |
| Lá | Mang phương thức thừa | Gọn sạch |
Ví dụ
- Input: gọi in trên thực đơn tổng. Output: mọi món mọi tầng in đúng 1 lần.
- Giải thích: yêu cầu lan đệ quy từ cụm cha xuống lá.
- Input: thêm con vào món lá ở bản trong suốt. Output: lá từ chối rõ ràng, cây không đổi.
- Giải thích: lá không có con nên phải báo lỗi nhẹ nhàng.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-067/.
Bài 1: Thực đơn dạng cây (trong suốt)
class MenuComponent {
public:
virtual std::string name() const = 0;
virtual void print(std::ostream &os, int indent = 0) const = 0;
virtual double price() const { throw std::logic_error("không hỗ trợ"); }
virtual bool vegetarian() const { throw std::logic_error("không hỗ trợ"); }
virtual void add(std::shared_ptr<MenuComponent>) { throw std::logic_error("không hỗ trợ"); }
virtual void remove(const MenuComponent *) { throw std::logic_error("không hỗ trợ"); }
virtual ~MenuComponent() = default;
};
class MenuItem : public MenuComponent { ... }; // lá
class Menu : public MenuComponent { ... }; // cụm, giữ vector con
Dựng cây: ALL MENUS gồm PANCAKE HOUSE, DINER, CAFE. DINER chứa thêm thực đơn con DESSERT.
allMenus.print() -> in mọi món mọi tầng, thụt lề theo độ sâu, mỗi món đúng 1 lần
item.add(...) -> ném logic_error, cây không đổi
Bài 2: Thao tác trên cả cây
Thêm các hàm đệ quy vào giao diện chung:
| Hàm | Lá | Cụm |
|---|---|---|
double totalPrice() | giá chính nó | tổng các con |
int countItems() | 1 | tổng các con |
int depth() | 0 | 1 + sâu nhất các con |
std::shared_ptr<MenuComponent> find(name) | so tên | tìm trong các con |
Bài 3: Bản an toàn và hệ thống file (nâng cao)
- Viết lại theo kiểu an toàn:
add/removechỉ có ởMenu. Viết 1 dòng code gọiaddtrên lá: bản an toàn không biên dịch được. Viết bảng so sánh 2 cách. - Mô hình thư mục:
File(name, size)vàDirectory. Viếtdu(tổng dung lượng),tree(in cây),findLarger(n)(các file lớn hơnnbyte). Đọc cây thư mục thật bằngstd::filesystemđể dựng Composite.
✅ Tiêu chí hoàn thành
- Test mẫu pass, in cây đúng thứ tự và thụt lề.
- Các hàm Bài 2 đúng trên cây 4 tầng.
- Bài 3 có bảng trong suốt vs an toàn và
dukhớp lệnhdu -sb(sai số do metadata). - Client in cây mà không có
dynamic_casthay kiểm tra kiểu.
❓ Câu hỏi ôn tập
- Định nghĩa Composite trong 1 câu là gì?
- Cụm và lá khác nhau thế nào?
- Trong suốt và an toàn khác nhau ở đâu?
- Lệnh in lan xuống cây ra sao?
🌕 Bạn đã hoàn thành ngày 67. Làm hết bài tập phía trên rồi sang ngày tiếp theo.