Strategy: đóng gói họ thuật toán bay và kêu của vịt
Định nghĩa Strategy, cấu trúc họ thuật toán hoán đổi được, và toàn bộ hành trình refactor SimUDuck từ kế thừa sụp đổ tới ủy thác linh hoạt.
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 đóng gói họ thuật toán. Bạn đổi hành vi vịt ngay khi chạy.
Kiến thức cần nhớ
Strategy là gì
- Gom mỗi thuật toán thành một lớp riêng.
- Các lớp cùng họ đổi chỗ được cho nhau.
- Ví dụ đời thường: thay lưỡi dao cùng một cán.
- Lớp dùng chỉ gọi, không cần biết chi tiết.
Câu chuyện SimUDuck
- Đầu tiên vịt kế thừa, vịt đồ chơi cũng biết bay sai.
- Tách giao diện thì logic bay bị sao chép khắp nơi.
- Strategy: vịt giữ 1 cách bay và 1 cách kêu.
- Ví dụ đời thường: gắn động cơ khác vào cùng thân xe.
Đổi hành vi lúc chạy
- Vịt gỗ gắn tên lửa là bay được ngay.
- Thêm kiểu bay mới chỉ thêm lớp mới.
- Lớp Vịt cũ không cần sửa chữ nào.
- Ví dụ đời thường: đổi sim là đổi nhà mạng.
| Cách làm | Thêm kiểu bay mới |
|---|---|
| Kế thừa | Sửa nhiều lớp |
| Giao diện thuần | Sửa mọi lớp liên quan |
| Strategy | Thêm 1 lớp mới |
Ví dụ
- Input: vịt mồi đang đứng yên, gắn tên lửa. Output: cùng nút bay nay bay được.
- Giải thích: chỉ thay đối tượng hành vi bên trong.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-006/.
Bài 1: SimUDuck với Strategy
Cài đặt đúng cấu trúc sau:
struct FlyBehavior { virtual std::string fly() const = 0; virtual ~FlyBehavior() = default; };
struct QuackBehavior { virtual std::string quack() const = 0; virtual ~QuackBehavior() = default; };
class Duck {
public:
Duck(std::unique_ptr<FlyBehavior> f, std::unique_ptr<QuackBehavior> q);
std::string performFly() const;
std::string performQuack() const;
void setFlyBehavior(std::unique_ptr<FlyBehavior> f); // đổi lúc chạy
void setQuackBehavior(std::unique_ptr<QuackBehavior> q);
virtual std::string display() const = 0;
virtual ~Duck() = default;
};
| Lớp cần viết | Trả về |
|---|---|
FlyWithWings, FlyNoWay, FlyRocketPowered | "bay bằng cánh", "không bay được", "bay bằng tên lửa" |
Quack, Squeak, MuteQuack | "quạc quạc", "chít chít", "..." |
MallardDuck, RubberDuck, DecoyDuck, ModelDuck | Mỗi lớp chọn hành vi mặc định trong constructor |
MallardDuck: performFly -> "bay bằng cánh", performQuack -> "quạc quạc"
RubberDuck: performFly -> "không bay được", performQuack -> "chít chít"
ModelDuck: performFly -> "không bay được"
ModelDuck.setFlyBehavior(FlyRocketPowered) -> performFly -> "bay bằng tên lửa"
Ràng buộc: lớp Duck không có if/switch theo loại vịt.
Bài 2: Refactor tính lương
Code ban đầu:
double salary(const std::string &type, double base, int hours, double sales) {
if (type == "fulltime") return base;
if (type == "parttime") return hours * 50000;
if (type == "sales") return base * 0.5 + sales * 0.1;
throw std::invalid_argument("unknown");
}
Yêu cầu: tạo interface PayPolicy với double pay(const Employee&) const. Viết 3 policy tương ứng. Employee giữ một PayPolicy. Sau đó thêm InternPolicy (lương cố định 3.000.000) mà không sửa code cũ.
fulltime base=15e6 -> 15000000
parttime hours=80 -> 4000000
sales base=10e6 sales=50e6 -> 10000000
intern -> 3000000
Bài 3: Nhân vật game đổi vũ khí (nâng cao)
Character có WeaponBehavior với int attack(int enemyArmor). Viết Sword (sát thương 30 trừ giáp), Bow (20, bỏ qua giáp), Magic (45 trừ 2 lần giáp, tối thiểu 5).
- Cho nhân vật đổi vũ khí giữa trận bằng
setWeapon. - Viết
mainmô phỏng 3 lượt đánh quái có giáp 10, mỗi lượt đổi vũ khí khác nhau. In sát thương từng lượt. - Trong
README.md, lập bảng so sánh: thêm vũ khí thứ 4 bằng kế thừa (SwordKnight,BowKnight...) tốn bao nhiêu lớp so với Strategy.
Sword vs armor 10 -> 20
Bow vs armor 10 -> 20
Magic vs armor 10 -> 25
Magic vs armor 30 -> 5
✅ Tiêu chí hoàn thành
- Toàn bộ test mẫu pass.
- Không có chuỗi
if/switchchọn hành vi trongDuck,Employee,Character. - Thêm hành vi mới chỉ tạo file mới, không sửa file cũ.
- Vẽ sơ đồ lớp (tay hoặc Mermaid) có quan hệ "has-a" tới 2 họ hành vi.
❓ Câu hỏi ôn tập
- Strategy là gì, nói 1 câu?
- Vì sao kế thừa gây bay nhầm ở vịt cao su?
- Đổi hành vi lúc chạy nghĩa là gì?
- Khi nào nên dùng Strategy?
🌕 Bạn đã hoàn thành ngày 6. Làm hết bài tập phía trên rồi sang ngày tiếp theo.