Bridge và Builder còn lại nhưng quan trọng
Bridge tách trừu tượng khỏi cài đặt cho remote TV, Builder dựng bữa ăn từng bước thay vì constructor khổng lồ.
KhóDesign pattern ~60 phút
Ôn lại trước khi học
Giới thiệu
Bài này học Bridge tách điều khiển khỏi thiết bị và Builder dựng từng bước. Dùng để thêm hãng TV hay món ăn không vỡ code.
Kiến thức cần nhớ
Bridge
- Định nghĩa: tách trừu tượng khỏi cài đặt qua một cầu.
- Remote giữ một cầu tới TV, bấm nút là chuyển lời.
- Ví dụ đời thường: như remote đa năng bấm là TV chạy.
- Thêm hãng TV mới chỉ thêm 1 lớp.
- Không nhân số lớp theo tích hai chiều.
| Cách làm | Thêm hãng mới | Thêm remote mới |
|---|---|---|
| Kế thừa liệt kê | Nhân thêm nhiều lớp | Nhân thêm nhiều lớp |
| Bridge qua cầu | Chỉ 1 lớp TV | Chỉ 1 lớp remote |
Builder
- Định nghĩa: dựng đối tượng phức tạp qua từng bước nhỏ.
- Thay constructor khổng lồ nhiều tham số rối rắm.
- Ví dụ đời thường: như gọi cơm chọn từng món một.
- Mỗi bước trả về chính builder để nối chuỗi.
- Bước cuối gọi build để ra thành phẩm.
# Builder dựng bữa ăn từng bước
meal = (MealBuilder()
.them_mon_chinh("cơm gà") # chọn món chính
.them_nuoc("trà đá") # chọn nước
.build()) # chốt đơn
print(meal)
Khi nào dùng
- Dùng Bridge khi hai chiều cùng thay đổi độc lập.
- Dùng Builder khi đối tượng có quá nhiều tùy chọn.
- Không dùng Bridge cho quan hệ một chiều đơn giản.
- Không dùng Builder cho object chỉ 1-2 field.
Ví dụ
- Input: thêm hãng TV thứ 4 -> Output: chỉ thêm 1 lớp TV mới.
- Giải thích: remote cũ giữ nguyên vì chỉ nói qua cầu.
- Input: bữa ăn thiếu nước -> Output: builder báo thiếu trước khi build.
- Giải thích: từng bước kiểm tra rõ hơn constructor dài.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-092/.
Bài 1: Bridge cho remote và TV
struct TV { // implementor
virtual void on() = 0; virtual void off() = 0; virtual void tuneChannel(int ch) = 0;
virtual int channel() const = 0; virtual ~TV() = default;
};
class RemoteControl { // abstraction
protected: TV &tv;
public:
explicit RemoteControl(TV &t);
virtual void on(); virtual void off(); virtual void setChannel(int ch);
virtual ~RemoteControl() = default;
};
class ConcreteRemote : public RemoteControl {
public: void nextChannel(); void previousChannel(); void favorite(int slot); void setFavorite(int slot, int ch);
};
TV cụ thể: SonyTV, SamsungTV, LgTV (kênh hợp lệ khác nhau: Sony 1-99, Samsung 1-999, LG 1-500; kênh sai thì ném lỗi).
ConcreteRemote(SonyTV).setChannel(5); nextChannel() -> Sony kênh 6
ConcreteRemote(SonyTV).setChannel(99); nextChannel() -> quay vòng về 1
Đếm số lớp cần viết với kế thừa thuần (mỗi loại remote x mỗi hãng) và với Bridge, khi có 3 loại remote và 3 hãng TV. Ghi vào README.md.
Bài 2: Builder cho đối tượng nhiều tùy chọn
class HttpRequest { /* method, url, headers, query, body, timeoutMs, retries */ };
class HttpRequestBuilder {
public:
explicit HttpRequestBuilder(std::string url);
HttpRequestBuilder &method(std::string m);
HttpRequestBuilder &header(std::string k, std::string v);
HttpRequestBuilder &query(std::string k, std::string v);
HttpRequestBuilder &body(std::string b);
HttpRequestBuilder &timeout(int ms);
HttpRequestBuilder &retries(int n);
HttpRequest build() const; // kiểm tra hợp lệ
};
Luật kiểm tra trong build() | Lỗi |
|---|---|
GET có body | invalid_argument |
timeout <= 0 hoặc retries < 0 | invalid_argument |
URL không bắt đầu bằng http:// hoặc https:// | invalid_argument |
Viết HttpRequest::toString() in dạng request HTTP thô. Test 5 request hợp lệ và 3 request lỗi.
Bài 3: Kết hợp và phản biện (nâng cao)
- Viết
ReportBuilderdựng báo cáo gồm tiêu đề, các mục, bảng. Kết hợp Bridge: báo cáo xuất quaRenderer(MarkdownRenderer,HtmlRenderer). - So
HttpRequestBuildervới constructor 7 tham số và với struct có designated initializers C++20. Viết 3 câu: khi nào Builder đáng giá, khi nào không.
✅ Tiêu chí hoàn thành
- Test Bridge pass với 3 hãng TV.
- Bảng số lớp kế thừa thuần vs Bridge.
- Builder kiểm tra đủ 3 luật, có test lỗi.
- Bài 3 có phản biện Builder bằng code thật.
❓ Câu hỏi ôn tập
- Bridge tách cái gì khỏi cái gì?
- Vì sao kế thừa liệt kê gây bùng nổ lớp?
- Builder thay thế nỗi đau nào?
- Khi nào không nên dùng Builder?
🌕 Bạn đã hoàn thành ngày 92. Làm hết bài tập phía trên rồi sang ngày tiếp theo.