Decorator: Starbuzz Coffee và chuỗi bọc
Định nghĩa Decorator, nguyên lý mở rộng không sửa đổi, và cách Starbuzz cộng dồn giá topping bằng chuỗi bọc.
Trung bìnhDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học cách cộng topping không vỡ lớp cũ. Bạn bọc đối tượng như búp bê Nga.
Kiến thức cần nhớ
Vấn đề của kế thừa
- Mỗi tổ hợp topping một lớp thì lớp bùng nổ.
- Nhét hết vào lớp cha thì lớp cha phình to.
- Thêm topping mới phải sửa code cũ.
- Ví dụ đời thường: in menu riêng cho mọi combo.
Chuỗi bọc cộng dồn
- Mỗi topping bọc đồ uống bên trong.
- Hỏi giá thì hỏi vào trong rồi cộng thêm.
- Hỏi mô tả thì nối thêm tên mình.
- Ví dụ đời thường: hộp quà lồng nhau, mở dần.
- Thêm topping mới chỉ thêm 1 lớp bọc.
Khi nào bọc, khi nào không
- Cần gắn thêm việc cho từng món riêng lẻ thì bọc.
- Số combo quá lớn không liệt kê nổi thì bọc.
- Chỉ 2-3 combo cố định thì liệt kê thẳng.
- Ví dụ đời thường: dán sticker lên cốc riêng.
| Cách làm | Thêm topping mới |
|---|---|
| Mỗi combo 1 lớp | Số lớp nhân đôi |
| Cờ ở lớp cha | Mở lớp cha ra sửa |
| Chuỗi bọc | Thêm 1 lớp mới |
Ví dụ
- Input: cà phê đậm + 1 sữa. Output: giá nền + 1 sữa, mô tả đủ 2 món.
- Giải thích: lớp sữa hỏi giá lõi rồi cộng phần mình.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-012/. Giá tính bằng đồng, kiểu long long.
Bài 1: Starbuzz Coffee
class Beverage {
public:
virtual std::string description() const = 0;
virtual long long cost() const = 0;
virtual ~Beverage() = default;
};
class CondimentDecorator : public Beverage {
protected:
std::unique_ptr<Beverage> inner;
public:
explicit CondimentDecorator(std::unique_ptr<Beverage> b);
};
| Lớp | Giá | Mô tả |
|---|---|---|
Espresso | 30.000 | "Espresso" |
HouseBlend | 25.000 | "House Blend" |
DarkRoast | 28.000 | "Dark Roast" |
Milk | +5.000 | nối ", Sữa" |
Mocha | +7.000 | nối ", Mocha" |
Soy | +6.000 | nối ", Sữa đậu nành" |
Whip | +4.000 | nối ", Kem" |
DarkRoast + Mocha + Mocha + Whip -> "Dark Roast, Mocha, Mocha, Kem" = 46000
Espresso -> "Espresso" = 30000
HouseBlend + Soy + Mocha + Whip -> 42000
Bài 2: Size ly và thêm topping mới
- Thêm
enum class Size { Small, Medium, Large }vàoBeverage. Topping lấy size từinner. Soytính giá theo size: nhỏ 4.000, vừa 6.000, lớn 8.000.- Thêm
Caramel(+9.000) chỉ bằng một file mới.
Espresso(Large) + Soy -> 38000
Espresso(Small) + Soy + Caramel -> 43000
Bài 3: Vé xem phim và decorator vào-ra (nâng cao)
Ticketgốc 90.000. Decorator:WeekendSurcharge(+20%),ThreeD(+30.000),StudentDiscount(-15%). Thứ tự bọc làm đổi giá: tính và giải thích 2 thứ tự khác nhau.- Viết interface
TextSource { virtual std::optional<char> read() = 0; }. ViếtStringSourcevà 2 decorator:UpperCase(đổi chữ thường thành hoa) vàSkipSpaces(bỏ khoảng trắng).
UpperCase(SkipSpaces(StringSource("a b c"))) đọc hết -> "ABC"
SkipSpaces(UpperCase(StringSource("x y"))) đọc hết -> "XY"
✅ Tiêu chí hoàn thành
- Test mẫu pass.
- Thêm
Caramelkhông sửa file cũ. - Bài 3 có bảng giá vé theo 2 thứ tự bọc kèm giải thích.
- Không rò bộ nhớ: dùng
unique_ptr, sanitizer sạch.
❓ Câu hỏi ôn tập
- Decorator là gì, nói 1 câu?
- Vì sao kế thừa gây bùng nổ lớp?
- Giá món bọc nhiều lớp tính thế nào?
- Khi nào không nên bọc?
🌕 Bạn đã hoàn thành ngày 12. Làm hết bài tập phía trên rồi sang ngày tiếp theo.