menu_book LESSON MODE

OOP Ch.9 — Composition & Design Patterns

calendar_today Đăng 29 May 2026
OOP Ch.9 — Composition & Design Patterns
school

NOW LEARNING

OOP Ch.9 — Composition & Design Patterns

🏗️ OOP Series · Lập trình Hướng đối tượng với Python
🧩
Chapter 9 · OOP Series

Composition
& Design Patterns

Ghép các object lại với nhau thay vì kế thừa — và học 2 design pattern phổ biến nhất!

🔗

Hai quan hệ cốt lõi trong OOP

🧬 Is-a (Inheritance)
🐕 Cho is-a DongVat
👨‍💼 QuanLy is-a NhanVien
🏃 VanDongVien is-a NguoiDung
→ Dùng kế thừa khi B là một loại của A
🧩 Has-a (Composition)
🚗 XeHoi has-a DongCo
👤 NguoiDung has-a DiaChi
📱 DienThoai has-a ManHinh
→ Dùng composition khi B là một phần của A
💡 Nguyên tắc vàng: "Favor Composition over Inheritance" — Khi không chắc chắn, hãy dùng composition. Nó linh hoạt hơn và ít gây rắc rối hơn về sau!
🚗

XeHoi = Engine + GPS + Radio + ...

class DongCo:
    def __init__(self, cc):
        self.cc = cc
        self.dang_chay = False
    def khoi_dong(self):
        self.dang_chay = True
        return f"🔥 Động cơ {self.cc}cc nổ máy!"
    def tat(self):
        self.dang_chay = False
        return "💤 Động cơ tắt."

class GPS:
    def dan_duong(self, dich):
        return f"🗺️  GPS: Đang dẫn đường đến {dich}..."

class DanAmThanh:
    def phat(self, bai):
        return f"🎵 Đang phát: {bai}"

# ── COMPOSITION ──────────────────────────
class XeHoi:
    def __init__(self, hang, cc):
        self.hang     = hang
        self.dong_co  = DongCo(cc)        ← HAS-A
        self.gps      = GPS()               ← HAS-A
        self.am_thanh = DanAmThanh()       ← HAS-A

    def bat_xe(self):
        print(self.dong_co.khoi_dong())

    def di_den(self, noi):
        print(self.gps.dan_duong(noi))

    def nghe_nhac(self, bai):
        print(self.am_thanh.phat(bai))

xe = XeHoi("Toyota", 2000)
xe.bat_xe()
xe.di_den("Hà Nội")
xe.nghe_nhac("Việt Nam Quê Hương Tôi")
🔥 Động cơ 2000cc nổ máy!
🗺️ GPS: Đang dẫn đường đến Hà Nội...
🎵 Đang phát: Việt Nam Quê Hương Tôi
🎯 🎯 Strategy Pattern

Đổi thuật toán lúc runtime không cần sửa code!

Strategy Pattern cho phép bạn thay đổi "cách làm" của một hành động mà không đụng vào class chính:

from abc import ABC, abstractmethod

# ── Các "chiến lược" sắp xếp ─────────────
class ChienLuocSapXep(ABC):
    @abstractmethod
    def sap_xep(self, ds): pass

class TangDan(ChienLuocSapXep):
    def sap_xep(self, ds):
        print("📈 Sắp xếp tăng dần")
        return sorted(ds)

class GiamDan(ChienLuocSapXep):
    def sap_xep(self, ds):
        print("📉 Sắp xếp giảm dần")
        return sorted(ds, reverse=True)

class TheoDoTuoi(ChienLuocSapXep):
    def sap_xep(self, ds):
        print("📊 Sắp xếp theo độ tuổi")
        return sorted(ds, key=lambda x: x["tuoi"])

# ── Class dùng strategy ──────────────────
class DanhSachHocSinh:
    def __init__(self):
        self.ds        = []
        self.chien_luoc = TangDan()    ← mặc định

    def doi_chien_luoc(self, cl):
        self.chien_luoc = cl            ← đổi runtime!

    def them(self, hs): self.ds.append(hs)

    def xuat(self):
        ketqua = self.chien_luoc.sap_xep(self.ds)
        for hs in ketqua:
            print(f"  {hs['ten']:10} {hs['tuoi']} tuổi")

ds = DanhSachHocSinh()
for hs in [
    {"ten":"Nam",  "tuoi":14},
    {"ten":"An",   "tuoi":13},
    {"ten":"Bình", "tuoi":15},
]: ds.them(hs)

ds.xuat()
ds.doi_chien_luoc(TheoDoTuoi())
ds.xuat()
📈 Sắp xếp tăng dần
An 13 tuổi
Bình 15 tuổi
Nam 14 tuổi
📊 Sắp xếp theo độ tuổi
An 13 tuổi
Nam 14 tuổi
Bình 15 tuổi
🏗️ 🏗️ Builder Pattern

Xây dựng object phức tạp từng bước

class NhanVatRPG:
    def __init__(self):
        self.ten  = "Unnamed"
        self.nghe = "Warrior"
        self.hp   = 100
        self.vu_khi = "Kiếm gỗ"
        self.ky_nang = []

    def __str__(self):
        return f"⚔️  [{self.nghe}] {self.ten} | HP:{self.hp} | {self.vu_khi}"

class NhanVatBuilder:
    def __init__(self):
        self.nv = NhanVatRPG()

    def ten(self, ten):
        self.nv.ten = ten
        return self                   ← trả về self để chain!

    def nghe(self, nghe):
        self.nv.nghe = nghe
        return self

    def hp(self, hp):
        self.nv.hp = hp
        return self

    def vu_khi(self, vk):
        self.nv.vu_khi = vk
        return self

    def them_ky_nang(self, kn):
        self.nv.ky_nang.append(kn)
        return self

    def build(self):
        return self.nv                ← trả về object hoàn chỉnh

# ✨ Method chaining — đọc như văn xuôi!
anh_hung = (
    NhanVatBuilder()
    .ten("Arthur")
    .nghe("Chiến Binh")
    .hp(200)
    .vu_khi("Kiếm Excalibur")
    .them_ky_nang("Chém lốc xoáy")
    .them_ky_nang("Khiên thần")
    .build()
)

print(anh_hung)
print("Kỹ năng:", anh_hung.ky_nang)
⚔️ [Chiến Binh] Arthur | HP:200 | Kiếm Excalibur
Kỹ năng: ['Chém lốc xoáy', 'Khiên thần']
💡
Method Chaining — Builder Pattern! Mỗi method trả về self để có thể gọi tiếp — tạo ra cú pháp "nối chuỗi" cực kỳ dễ đọc. Rất phổ biến trong các thư viện Python hiện đại!

🧩 Kiểm tra nhanh!

Chapter 9 — Composition & Design Patterns

1️⃣Khác nhau giữa Is-a và Has-a?
→ Is-a = kế thừa, Has-a = composition
2️⃣Strategy Pattern giải quyết vấn đề gì?
→ Đổi thuật toán lúc runtime
3️⃣Builder Pattern dùng khi nào?
→ Tạo object phức tạp nhiều bước
4️⃣Method chaining hoạt động nhờ gì?
→ Mỗi method return self
OOP Series: Chapter 9 / 10
🧩
🏆
Chương cuối · Chapter 10
Dự Án Tổng Kết — RPG Mini Game!
Kết hợp toàn bộ 9 chapter: Class, Methods, Encapsulation, Inheritance, Polymorphism, Magic Methods, Abstract, Composition vào một trò chơi RPG hoàn chỉnh!

NEXT STEP

Tiếp theo nên học gì

route