Project: refactor SimUDuck bằng Strategy và Observer

Đề refactor đàn vịt kế thừa cứng nhắc sang Strategy và gắn bảng quan sát, kèm rubric chấm bốn mức.
KhóDesign pattern ~120 phút

Giới thiệu

Bài này refactor đàn vịt kế thừa cứng sang ủy thác. Dùng để đổi cánh tên lửa lúc chạy không vỡ code.

Kiến thức cần nhớ

Mùi code cũ

  • Lớp cha ôm bay kêu bơi hiển thị tất cả.
  • Vịt cao su kế thừa nên biết bay sai bét.
  • Logic bay lặp lại ở nhiều lớp con khác.
  • Ví dụ đời thường: như 1 remote điều khiển cả nhà sai.
  • Thêm vịt mới là phải mở lớp cũ ra vá.

Strategy tách bay kêu

  • Tách họ bay: cánh, không bay, tên lửa riêng.
  • Tách họ kêu: quạc, chíp, im lặng riêng biệt.
  • Vịt chỉ giữ 2 tham chiếu rồi ủy thác việc.
  • Ví dụ đời thường: như thay pin thay lưỡi dao rời.
  • Đổi hành vi lúc chạy mà vịt khác không ảnh hưởng.
ViệcCode cũCode mới
Thêm vịtSửa lớp chaThêm lớp mới
Đổi bayGhi đè vá víuGán hành vi mới
Đếm kêuĐếm tay thủ côngObserver báo tự động

Observer đếm báo

  • Mỗi vịt là 1 chủ đề nhỏ phát tin kêu.
  • Bảng đếm và nhà nghiên cứu đăng ký nghe tin.
  • Mỗi tiếng kêu là 1 tin báo tự động gửi.
  • Ví dụ đời thường: như follow vịt là nhận tiếng kêu.
  • Bảng mới chỉ cần đăng ký, không sửa vịt.

Ví dụ

  • Input: vịt mồi đang chạy xin cánh tên lửa -> Output: chỉ nó bay đổi.
  • Giải thích: ủy thác nên gán lại 1 tham chiếu là xong.
# Đổi hành vi lúc chạy không sửa lớp
vit_moi.kieu_bay = TenLua()  # gán hành vi mới
vit_moi.bay()  # bay tên lửa ngay

💻 Bài tập

Ngôn ngữ: C++17 (hoặc Java). Đây là project, dành 90 phút. Nộp vào day-116-simuduck/.

Bài 1: Code cũ cần refactor

Viết lại đúng phiên bản "trước refactor" trong before/:

class Duck {
 public:
  virtual std::string display() const = 0;
  std::string quack() const { return "Quạc"; }
  std::string fly() const { return "Bay bằng cánh"; }
  std::string swim() const { return "Bơi"; }
};
class RubberDuck : public Duck { /* ghi đè quack bằng "Chít", fly... không ghi đè được vì không virtual */ };
class DecoyDuck : public Duck { ... };

Viết test before_test.cpp chỉ ra 3 lỗi: vịt cao su bay được, vịt mồi kêu được, muốn đổi hành vi lúc chạy thì không thể.

Bài 2: Refactor bằng Strategy và Observer

Thư mục after/:

Yêu cầuChi tiết
Strategy2 họ FlyBehavior, QuackBehavior. Vịt giữ con trỏ, set... đổi lúc chạy
ObserverMỗi vịt là subject. QuackCounterBoard đếm tổng tiếng kêu. ResearcherLog ghi "<tên>: <tiếng kêu>"
Không phá hành viMọi hành vi đúng của before/ giữ nguyên
Mở rộngThêm RobotDuck (bay bằng tên lửa, kêu "Bíp") chỉ bằng file mới
RubberDuck.performFly()                        -> "Không bay được"
DecoyDuck.setFlyBehavior(Rocket); performFly() -> "Bay bằng tên lửa"; các vịt khác không đổi
3 vịt kêu mỗi con 2 lần                         -> board đếm 6, log có 6 dòng

Bài 3: Báo cáo refactor (nâng cao)

Viết REFACTOR.md:

  1. Sơ đồ lớp trước và sau (Mermaid).
  2. Bảng: yêu cầu thay đổi, số file phải sửa ở before/, số file phải sửa ở after/. Ít nhất 4 yêu cầu (thêm vịt, thêm kiểu bay, đổi bay lúc chạy, thêm người quan sát).
  3. Nguyên lý thiết kế đã áp dụng (đóng gói phần thay đổi, ưu tiên composition, lập trình theo interface) và chỗ áp dụng trong code.

✅ Tiêu chí hoàn thành

  • before_test.cpp chỉ ra đủ 3 lỗi.
  • Test after/ pass, RobotDuck chỉ là file mới.
  • REFACTOR.md có 2 sơ đồ và bảng 4 yêu cầu.
  • Commit tách rõ: code cũ, test lỗi, refactor Strategy, thêm Observer.

❓ Câu hỏi ôn tập

  • Mùi lớn nhất của kế thừa là gì?
  • Strategy tách cái gì ra?
  • Ai nhận tin khi vịt kêu?
  • Thêm bảng mới có sửa vịt không?

🌕 Bạn đã hoàn thành ngày 116. Làm hết bài tập phía trên rồi sang ngày tiếp theo.

Bớt paste mù, hiểu sâu từng dòng mình viết.

180 Days Challenge. 180 ngày cai nghiện vibe code.