Đàn vịt compound: 4 patterns trong một app
Mô phỏng vịt dùng chung Strategy Observer Decorator Factory, đếm tiếng quạc và đàn vịt an toàn.
KhóDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học ghép 4 pattern trong 1 app vịt. Dùng để đếm tiếng kêu không sửa lớp vịt cũ.
Kiến thức cần nhớ
Strategy và Decorator
- Strategy: mỗi vịt giữ kiểu kêu và bay riêng.
- Decorator đếm: bọc vịt thật, kêu thì tăng đếm 1.
- Ví dụ đời thường: như đeo vòng đếm bước không mổ xẻ.
- Không sửa lớp vịt nào mà vẫn đếm được.
- Kêu thật trước, tăng đếm ngay sau đó.
Factory và Composite
- Factory đếm: xưởng tạo vịt đã bọc sẵn bộ đếm.
- Khách gọi xưởng như gọi vịt thường mỗi ngày.
- Composite đàn: đàn là 1 vịt chứa nhiều vịt con.
- Gọi kêu trên đàn thì cả đàn cùng kêu.
- Ví dụ đời thường: như hô 1 lớp là cả lớp thưa.
| Pattern | Việc trong app | Ví như |
|---|---|---|
| Strategy | Giữ kiểu kêu bay | Tính cách mỗi vịt |
| Decorator | Đếm tiếng kêu | Vòng đếm bước |
| Factory | Tạo vịt đã bọc | Dây chuyền đóng gói |
| Composite | Gom đàn vịt | Lớp học nhiều tổ |
Observer
- Định nghĩa: nhà nghiên cứu đăng ký nghe từng vịt.
- Mỗi tiếng quạc là 1 tin báo tự động tới.
- Ví dụ đời thường: như follow kênh là nhận tin mới.
- Thêm người nghe không sửa code vịt cũ.
- Đàn kêu thì mỗi con báo riêng 1 tin.
Ví dụ
- Input: đàn 3 vịt kêu 1 lượt -> Output: đếm tổng tăng 3.
- Giải thích: mỗi bộ bọc tăng 1 rồi cộng dồn lại.
# Bộ đếm bọc ngoài vịt thật
class DemQuac:
def keu(self):
self.vit.keu() # vịt trong kêu
DemQuac.tong += 1 # tăng đếm chung
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-108/. Bài này xây dần từng pattern, mỗi bước 1 commit.
Bài 1: Vịt, ngỗng và đếm tiếng kêu
struct Quackable { virtual std::string quack() = 0; virtual ~Quackable() = default; };
| Bước | Việc phải làm | Pattern |
|---|---|---|
| 1 | MallardDuck, RedheadDuck, DuckCall, RubberDuck cài Quackable | |
| 2 | Goose có honk(). Viết GooseAdapter để ngỗng vào được đàn | Adapter |
| 3 | QuackCounter bọc 1 Quackable, mỗi lần kêu tăng biến đếm static | Decorator |
| 4 | AbstractDuckFactory với CountingDuckFactory trả vịt đã bọc bộ đếm | Abstract Factory |
Tạo 4 vịt qua CountingDuckFactory + 1 GooseAdapter không bọc đếm
Mỗi con kêu 1 lần -> QuackCounter::count() = 4 (ngỗng không được đếm)
Bài 2: Đàn vịt và người quan sát
| Bước | Việc phải làm | Pattern |
|---|---|---|
| 5 | Flock : Quackable chứa std::vector<std::shared_ptr<Quackable>>. quack() gọi mọi con | Composite |
| 6 | Iterator duyệt đàn (có thể dùng iterator của std::vector) | Iterator |
| 7 | QuackObservable với registerObserver, notifyObservers. Quackologist in "Nhà nghiên cứu: <tên> vừa kêu" | Observer |
Đàn lớn = 3 vịt + đàn vịt trời (4 con) -> quack() -> đếm tăng 7
Nhà nghiên cứu đăng ký cả đàn lớn -> nhận đúng 7 tin, mỗi tin ghi tên vịt thật (không phải "QuackCounter")
Khi
QuackCounter bọc vịt, tin báo phải mang tên vịt bên trong. Hãy để decorator chuyển lời registerObserver vào vịt được bọc.Bài 3: Nhìn lại (nâng cao)
- Vẽ sơ đồ lớp cả hệ thống (Mermaid
classDiagram), đánh dấu 6 pattern. - Viết 1 câu cho mỗi pattern: vấn đề nó giải quyết trong bài này.
- Phản biện: đâu là chỗ dùng pattern thừa nếu yêu cầu chỉ là "đếm tiếng kêu của 5 con vịt"? Viết bản tối giản và so số dòng.
✅ Tiêu chí hoàn thành
- Mỗi bước có 1 commit, test của bước trước vẫn pass.
- Test đếm và test tin báo của Bài 2 pass.
- Sơ đồ lớp đánh dấu đủ 6 pattern.
- Có phản biện và bản tối giản.
❓ Câu hỏi ôn tập
- Ai đếm tiếng quạc?
- Xưởng tạo ra hàng gì?
- Đàn kêu thì ai kêu?
- Tin báo gửi tới ai?
🌕 Bạn đã hoàn thành ngày 108. Làm hết bài tập phía trên rồi sang ngày tiếp theo.