Giao diện
Phần 14: Design Patterns Kiến trúc
"Kiến trúc tốt biến phức tạp thành đơn giản."
Áp dụng các GoF patterns trong ngữ cảnh Modern C++20 và Hệ thống Phân tán. Vượt qua ví dụ "Cat kế thừa Animal".
Áp dụng các GoF patterns trong ngữ cảnh Modern C++20 và Hệ thống Phân tán. Vượt qua ví dụ "Cat kế thừa Animal".
Phân tích Kiến trúc: Tại sao Kế thừa Thất bại
Ác mộng của Inheritance
cpp
// ❌ ÁC MỘNG KẾ THỪA — Thực tế ở các dự án lớn
class TunnelBase { /* logic cơ bản */ };
class TcpTunnel : public TunnelBase { /* chi tiết TCP */ };
class SecureTcpTunnel : public TcpTunnel { /* thêm mã hóa */ };
class CompressedSecureTcpTunnel : public SecureTcpTunnel { /* thêm nén */ };
class LoggedCompressedSecureTcpTunnel : public CompressedSecureTcpTunnel { /* thêm log */ };
// Sâu 5 tầng! Còn gì có thể sai sót?💀 CÁC VẤN ĐỀ VỚI KẾ THỪA SÂU
- Lớp Cơ sở Mong manh — Thay đổi
TunnelBasephá vỡ TẤT CẢ lớp con - Bùng nổ Tổ hợp — Muốn Logged + Compressed nhưng KHÔNG Secure? Tạo class mới!
- Vấn đề Kim cương — Nhiều đường kế thừa → Hỗn loạn
- Không thể Kiểm thử — Mock cần mock TOÀN BỘ cây kế thừa
- Thời gian Biên dịch — Thay đổi base → rebuild mọi thứ
Giải pháp: Tổ hợp thay vì Kế thừa
cpp
// ✅ CÁCH TỔ HỢP — "Has-A" thay vì "Is-A"
class Tunnel {
public:
Tunnel(std::unique_ptr<IEncryption> enc,
std::unique_ptr<ICompression> comp,
std::unique_ptr<ILogger> logger)
: encryption_(std::move(enc))
, compression_(std::move(comp))
, logger_(std::move(logger)) {}
void Send(const Data& data) {
auto compressed = compression_->Compress(data);
auto encrypted = encryption_->Encrypt(compressed);
logger_->Log("Đang gửi " + std::to_string(data.size()) + " bytes");
DoSend(encrypted);
}
private:
std::unique_ptr<IEncryption> encryption_;
std::unique_ptr<ICompression> compression_;
std::unique_ptr<ILogger> logger_;
};
// Kết hợp BẤT KỲ tính năng nào!
auto tunnel = std::make_unique<Tunnel>(
std::make_unique<AES256Encryption>(), // hoặc NoEncryption
std::make_unique<ZstdCompression>(), // hoặc LZ4Compression
std::make_unique<AsyncFileLogger>() // hoặc NullLogger
);┌─────────────────────────────────────────────────────────────────────────┐
│ KẾ THỪA vs TỔ HỢP │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ KẾ THỪA (Is-A): TỔ HỢP (Has-A): │
│ ─────────────────── ──────────────────── │
│ TunnelBase ┌─────────────────┐ │
│ ↑ │ Tunnel │ │
│ TcpTunnel │ ┌───────────┐ │ │
│ ↑ │ │IEncryption│──┼──► AES, None, RSA │
│ SecureTcpTunnel │ └───────────┘ │ │
│ ↑ │ ┌───────────┐ │ │
│ Compressed... │ │ICompress │──┼──► Zstd, LZ4, None │
│ ↑ │ └───────────┘ │ │
│ Logged... │ ┌───────────┐ │ │
│ │ │ILogger │──┼──► File, Console │
│ Vấn đề: │ └───────────┘ │ │
│ • Cứng nhắc & Mong manh └─────────────────┘ │
│ • Khó kiểm thử Lợi ích: │
│ • Bùng nổ tổ hợp • Linh hoạt, Dễ kiểm thử │
│ • Kết hợp linh hoạt các tính năng │
│ • Dễ mock từng phần riêng lẻ │
│ │
└─────────────────────────────────────────────────────────────────────────┘Phân loại Pattern
┌─────────────────────────────────────────────────────────────────────────┐
│ TỔNG QUAN DESIGN PATTERNS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ STRUCTURAL PATTERNS (Cách tổ chức lớp) │
│ ───────────────────────────────────────── │
│ • Pimpl Idiom → Ổn định ABI, Tường lửa Compile-time │
│ • Facade → Đơn giản hóa hệ thống con phức tạp │
│ • Adapter → Kết nối các interface không tương thích │
│ │
│ CREATIONAL PATTERNS (Cách tạo đối tượng) │
│ ──────────────────────────────────────────── │
│ • Singleton → Một instance duy nhất, thread-safe (Meyers') │
│ • Factory → Tạo mà không chỉ định class cụ thể │
│ • Builder → Xây dựng đối tượng phức tạp │
│ │
│ BEHAVIORAL PATTERNS (Cách đối tượng giao tiếp) │
│ ─────────────────────────────────────────── │
│ • Observer → Pub/Sub, Event-driven │
│ • Strategy → Chuyển đổi thuật toán lúc runtime │
│ • Command → Đóng gói request thành đối tượng │
│ │
└─────────────────────────────────────────────────────────────────────────┘Cấu trúc Module
| Bài học | Pattern | Ứng dụng |
|---|---|---|
| 🧱 Pimpl Idiom | Structural | Ổn định ABI, Thiết kế SDK |
| 🔒 Singleton | Creational | Thread-safe Global State |
| 📡 Observer | Behavioral | Hệ thống Sự kiện, Pub/Sub |
| 🔀 Strategy | Behavioral | Chuyển đổi Thuật toán |
Khi nào Sử dụng Design Patterns
┌─────────────────────────────────────────────────────────────────────────┐
│ HƯỚNG DẪN CHỌN PATTERN │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ NẾU BẠN CẦN... SỬ DỤNG... │
│ ────────────────────────────── ────────────────────────────────── │
│ Ẩn chi tiết implementation → Pimpl Idiom │
│ ABI ổn định cho SDK → Pimpl Idiom │
│ Giảm thời gian biên dịch → Pimpl Idiom │
│ │
│ Instance đơn toàn cục → Meyers' Singleton │
│ Khởi tạo lazy → Meyers' Singleton │
│ Xây dựng thread-safe → Meyers' Singleton │
│ │
│ Thông báo nhiều listener → Observer Pattern │
│ Tách rời sender khỏi receiver → Observer Pattern │
│ Kiến trúc event-driven → Observer Pattern │
│ │
│ Chuyển đổi thuật toán runtime → Strategy Pattern │
│ Tránh chuỗi switch/if → Strategy Pattern │
│ Nguyên tắc Open/Closed → Strategy Pattern │
│ │
└─────────────────────────────────────────────────────────────────────────┘Điều kiện Tiên quyết
Trước khi học module này, bạn cần:
- ✅ Phần 9: Quản lý Bộ nhớ — Smart Pointers
- ✅ Phần 5: Templates — Lập trình Generic
- ✅ Phần 12: Testing — Mocking với GMock