Chain of Responsibility và Flyweight
Chuỗi xử lý yêu cầu mua thư viện email, và chia sẻ nét vẽ cây trong rừng ngàn cây của Flyweight.
KhóDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học chuyền yêu cầu qua chuỗi và chia sẻ object. Dùng để duyệt mua nhiều cấp và vẽ rừng ngàn cây.
Kiến thức cần nhớ
Chain of Responsibility
- Định nghĩa: chuyền yêu cầu dọc chuỗi tới khi có người xử lý.
- Mỗi mắt xích tự xử hoặc chuyền cho người kế tiếp.
- Ví dụ đời thường: như đơn xin duyệt qua từng cấp trên.
- Người gửi chỉ đưa đầu chuỗi, không cần biết ai xử.
- Thêm cấp mới chỉ cần chen 1 mắt xích vào.
Ví dụ chuỗi duyệt mua
- Trưởng nhóm duyệt dưới 1 triệu.
- Trưởng phòng duyệt dưới 10 triệu.
- Giám đốc duyệt mọi mức còn lại.
- Ví dụ đời thường: đơn 5 triệu dừng ở trưởng phòng.
- Cần mắt chặn cuối để đơn không rơi mất.
| Mắt xích | Hạn mức | Việc làm |
|---|---|---|
| Trưởng nhóm | Dưới 1 triệu | Tự duyệt hoặc chuyền |
| Trưởng phòng | Dưới 10 triệu | Tự duyệt hoặc chuyền |
| Giám đốc | Mọi mức còn lại | Chặn cuối chuỗi |
Flyweight
- Định nghĩa: chia sẻ phần chung, tách riêng phần khác biệt.
- Ngàn cây chung vài nét vẽ, khác vị trí và màu.
- Ví dụ đời thường: như dùng chung khuôn in thay ngàn bản.
- Cache phần chung nên RAM giảm mạnh.
- Phần riêng lưu ngoài, truyền vào khi vẽ.
Ví dụ
- Input: đơn mua 5 triệu -> Output: trưởng phòng duyệt.
- Giải thích: trưởng nhóm chuyền, trưởng phòng đủ hạn mức xử lý.
# Mắt xích tự xử hoặc chuyền tiếp
def xu_ly(yeu_cau):
if yeu_cau <= han_muc: # đủ quyền thì xử
return "duyệt"
return nguoi_ke.xu_ly(yeu_cau) # không thì chuyền
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-104/.
Bài 1: Chuỗi duyệt chi tiêu
struct Request { std::string id; long long amount; std::string purpose; };
class Approver {
protected: std::shared_ptr<Approver> next;
public:
std::shared_ptr<Approver> setNext(std::shared_ptr<Approver> n); // trả n để nối chuỗi
virtual std::string handle(const Request &r); // mặc định: chuyền tiếp
virtual ~Approver() = default;
};
| Mắt xích | Duyệt khi |
|---|---|
TeamLead | amount < 1.000.000 |
Manager | amount < 10.000.000 |
Director | amount < 100.000.000 |
Board (chặn cuối) | Mọi mức còn lại, trả "cần họp HĐQT" |
5.000.000 -> "Manager duyệt"
500.000 -> "TeamLead duyệt"
2 tỉ -> "cần họp HĐQT"
Chuỗi không có Board, 2 tỉ -> "không ai xử lý" (không crash, không rơi mất)
Chen thêm FraudCheck ở đầu chuỗi: từ chối nếu purpose chứa từ khóa đen. Không sửa lớp nào khác.
Bài 2: Middleware HTTP
Viết chuỗi xử lý request giả lập: Logging -> Auth -> RateLimit -> Router.
Authtừ chối (401) nếu thiếu headerAuthorization.RateLimittừ chối (429) nếu 1 IP quá 5 request/giây (dùng đồng hồ giả).Loggingghi lại mọi request kể cả bị từ chối ở sau (cần xử lý khi chuỗi trả về).
Bài 3: Flyweight cho rừng cây (nâng cao)
struct TreeType { std::string name; std::string color; std::vector<uint8_t> texture; }; // texture 64 KB
struct Tree { int x, y; const TreeType *type; };
class TreeFactory { public: const TreeType *get(const std::string &name, const std::string &color); size_t typeCount() const; };
- Trồng 1.000.000 cây với 5 loại. In bộ nhớ ước lượng khi mỗi cây giữ texture riêng và khi dùng Flyweight.
typeCount()phải bằng 5.- Đo bộ nhớ thật bằng
/proc/self/status(VmRSS) cho 2 cách.
✅ Tiêu chí hoàn thành
- Test chuỗi duyệt pass, không yêu cầu nào rơi mất.
- Chen
FraudCheckkhông sửa lớp cũ. - Middleware ghi log cả request bị từ chối.
- Bảng bộ nhớ Flyweight có số thật.
❓ Câu hỏi ôn tập
- Ai quyết định chuyền trong chain?
- Mắt chặn cuối để làm gì?
- Flyweight chia sẻ cái gì?
- Phần riêng lưu ở đâu?
🌕 Bạn đã hoàn thành ngày 104. Làm hết bài tập phía trên rồi sang ngày tiếp theo.