Facade: rạp hát tại gia và Least Knowledge
Ôn lại trước khi học
Giới thiệu
Bài này học Facade gom hệ phức tạp thành nút bấm đơn giản. Mặt tiền này giúp dùng rạp hát tại gia dễ dàng.
Kiến thức cần nhớ
Facade là gì
- Facade là lớp mặt tiền đơn giản trước hệ con phức tạp.
- Người dùng gọi mặt tiền thay vì gọi từng thiết bị.
- Ví dụ đời thường: điều khiển tivi có một nút xem phim.
- Dùng khi nhiều bước phải chạy đúng thứ tự.
Rạp hát hai nút bấm
- Xem phim phải bật 6 thiết bị theo đúng thứ tự.
- Nút
xemPhim()chạy đủ 10 bước nhỏ tự động. - Nút
ketThuc()tắt ngược lại cho an toàn. - Ví dụ đời thường: bấm một nút là đèn tắt, phim chạy.
# Mặt tiền gom nhiều bước thành 1 nút
class HomeTheater:
def xem_phim(self):
self.bap_nuoc() # bước 1: bật máy bắp
self.ha_den() # bước 2: hạ đèn
self.bat_may_chieu() # bước 3: bật máy chiếu
self.bat_phim() # bước 4: chạy phim
Least Knowledge nói với bạn thân
- Chỉ gọi đối tượng mình giữ trực tiếp.
- Không gọi dây chuyền kiểu
a.b.c.bat(). - Ví dụ đời thường: nhờ lễ tân, không tự vào kho.
- Chuỗi gọi dài làm đổi một chỗ vỡ cả chuỗi.
| Khía cạnh | Adapter | Facade |
|---|---|---|
| Mục đích | Dịch 1 giao diện | Gom cả hệ con |
| Giao diện cũ | Ẩn đi | Vẫn mở cho ai cần |
| Số bước | Thường 1 bước | Nhiều bước đúng thứ tự |
Ví dụ
- Input: bấm
xemPhim()khi mọi máy đang tắt -> Output: phim chạy, đèn hạ, máy chiếu bật. - Câu này chứng minh mười bước gom thành một nút.
- Input: bấm
ketThuc()sau khi xem -> Output: mọi máy tắt theo thứ tự ngược. - Câu này chứng minh tắt máy cũng an toàn và gọn.
💻 Bài tập
Ngôn ngữ: C++17 (hoặc Java). Nộp vào day-031/. Mỗi thiết bị ghi hành động vào một std::vector<std::string> &log chung để test thứ tự.
Bài 1: Home Theater Facade
| Thiết bị | Hàm |
|---|---|
Amplifier | on(), off(), setStreamingPlayer(StreamingPlayer&), setSurroundSound(), setVolume(int) |
StreamingPlayer | on(), off(), play(movie), stop() |
Projector | on(), off(), wideScreenMode() |
Screen | down(), up() |
TheaterLights | on(), dim(int percent) |
PopcornPopper | on(), off(), pop() |
class HomeTheaterFacade {
public:
HomeTheaterFacade(Amplifier&, StreamingPlayer&, Projector&, Screen&, TheaterLights&, PopcornPopper&);
void watchMovie(const std::string &movie); // đủ các bước theo đúng thứ tự log bên dưới
void endMovie(); // tắt ngược lại
};
watchMovie("Dune") -> log:
popper.on, popper.pop, lights.dim(10), screen.down, projector.on,
projector.wideScreenMode, amp.on, amp.setStreamingPlayer, amp.setSurroundSound, amp.setVolume(5),
player.on, player.play(Dune)
endMovie() -> popper.off, lights.on, screen.up, projector.off, amp.off, player.stop, player.off
Ràng buộc: gọi watchMovie 2 lần liên tiếp không bật lại thiết bị đang bật (Facade nhớ trạng thái).
Bài 2: Sửa vi phạm Least Knowledge
Code sau vi phạm nguyên lý:
double Station::temperature() { return thermometer.getSensor().getReading().celsius(); }
std::string Order::customerCity() { return customer.getAddress().getCity().getName(); }
Viết lại để mỗi hàm chỉ gọi "bạn thân". Viết 2 câu cho mỗi hàm: nếu lớp Reading đổi thì bản cũ vỡ ở đâu, bản mới vỡ ở đâu.
Bài 3: Facade cho thư viện phức tạp (nâng cao)
Giả lập hệ đặt vé với 4 lớp: SeatService, PaymentGateway, EmailService, LoyaltyService. Đặt vé cần: giữ ghế, thanh toán, cộng điểm, gửi email.
Viết BookingFacade::book(userId, showId, seatCount):
- Thanh toán lỗi: nhả ghế đã giữ, không cộng điểm, không gửi email.
- Gửi email lỗi: vé vẫn hợp lệ, ghi cảnh báo.
- Trả
BookingResult { bool ok; std::string reason; }.
Viết 3 test cho 3 kịch bản: thành công, thanh toán lỗi, email lỗi.
✅ Tiêu chí hoàn thành
- Test thứ tự log của Bài 1 pass.
- Client vẫn gọi thẳng được từng thiết bị nếu cần (Facade không khóa hệ con).
- Bài 3 có 3 test, thanh toán lỗi thì ghế được nhả.
- Phân biệt được Facade, Adapter, Decorator bằng một câu mỗi pattern.
❓ Câu hỏi ôn tập
- Facade là gì và mặt tiền giúp gì?
- Nút xem phim gom những bước nào?
- Least Knowledge cấm kiểu gọi nào?
- Khi nào không nên dùng Facade?
🌕 Bạn đã hoàn thành ngày 31. Làm hết bài tập phía trên rồi sang ngày tiếp theo.