Skip to content

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++20Hệ 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

  1. Lớp Cơ sở Mong manh — Thay đổi TunnelBase phá vỡ TẤT CẢ lớp con
  2. Bùng nổ Tổ hợp — Muốn Logged + Compressed nhưng KHÔNG Secure? Tạo class mới!
  3. Vấn đề Kim cương — Nhiều đường kế thừa → Hỗn loạn
  4. Không thể Kiểm thử — Mock cần mock TOÀN BỘ cây kế thừa
  5. 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ọcPatternỨng dụng
🧱 Pimpl IdiomStructuralỔn định ABI, Thiết kế SDK
🔒 SingletonCreationalThread-safe Global State
📡 ObserverBehavioralHệ thống Sự kiện, Pub/Sub
🔀 StrategyBehavioralChuyể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:


Bước tiếp theo

🧱 Pimpl Idiom → — Ổn định ABI, Tường lửa Compile-time